DirectX (22.)

Jak jsem slφbil v minulΘ lekci, dnes se budeme v∞novat v∞tÜφmu projektu, kter² jsem pro Vßs p°ipravil a kter² tu podrobn∞ji rozebereme.

22.1. Projekt D3DEngine1

Projekt bude mφt podobnou strukturu jako projekt z minul²ch lekcφ o DirectDraw. Dnes zatφm vytvo°φme t°i pod-projekty: Display, Tester a Setup. Navφc jeÜt∞ budeme pou₧φvat knihovnu Common, ani₧ bychom ji vklßdali do projektu.

Co bude nßÜ projekt zatφm um∞t? Nic moc, ale postupn∞ budeme p°idßvat novΘ funkce, kterΘ budeme testovat pomocφ aplikace Tester. Dnes se tedy budeme zab²vat p°edevÜφm projektem Display. V²sledkem projektu Setup bude aplikace Setup.exe, kterou pou₧ijeme k nastavenφ grafiky na konkrΘtnφm PC. Program generuje soubor Setup.ini, kde jsou vÜechny tyto nastavenφ ulo₧eny. To pak pou₧ijeme v naÜem "enginu" pro sprßvnΘ nastavenφ vlastnostφ device atd. Touto aplikacφ se podrobn∞ji zab²vat nebudeme.

Nechte-li projekt psßt sami, m∙₧ete pou₧φt ten, kter² najdete v sekci Downloads. Projekt mß nßsledujφcφ strukturu:

Pro ty, co si cht∞jφ projekt vytvo°it sami jen up°esnφm typy jednotliv²ch aplikacφ. Nepou₧φvßme ₧ßdnΘ MFC, tak₧e vÜechny projekty jsou v²hradn∞ Win32 API. Display je dynamickß knihovna a Tester aplikace. Nezapome≥te nastavit sprßvn∞ zßvislosti (Dependencies) a takΘ cestu k v²stupnφm soubor∙m jako spoleΦn² adresß° Debug Φi Release. VÜechny projekty musφ vklßdat knihovnu Common.dll pomocφ Common.lib (tento soubor najdete u projektu v sekci Downloads). Dßle Display a Setup vklßdajφ knihovny d3d8.lib a D3dx8.lib, abychom v nich mohli pou₧φt funkce Direct3D. Projektem Setup se zde zab²vat nebudu, tak₧e ho zkopφrujte a vlo₧te jako existujφcφ projekt.

V dalÜφ Φßsti si povφme trochu teorie o ztrßt∞ za°φzenφ.

22.2. Ztrßta za°φzenφ

Pamatujete si na ztrßty povrch∙ v DirectDraw? Bohu₧el ani v D3D se nevyhneme problΘm∙m, pokud se u₧ivatel p°epne nßsilφm z fullscreenu do Windows. V Direct3D nastane tzv. ztrßta za°φzenφ. V tomto stavu je za°φzenφ nepou₧itelnΘ a je pot°eba ud∞lat pßr krok∙ k jeho nßprav∞.

Poznßmka: Pokud jedete ve window re₧imu, m∙₧e dojφt ke ztrßt∞ takΘ a to tak, ₧e zßrove≥ spustφte aplikaci, kterß p∙jde do fullscreenu.

Co musφte ud∞lat pro obnovu? Tyto t°i kroky:

  1. Uvolnit vÜechny zdroje - textury, meshe, shadery, index a vertex buffery.

  2. Resetovat za°φzenφ a chvilku poΦkat.

  3. Obnovit vÜechny zdroje tj. znovu naΦφst vÜechny textury, meshe atd.

Proto budou mφt vÜechny objekty, kterΘ majφ charakter zdroje, metodu Release(), kterß objekt uvolnφ a metodu Restore(), kter² po₧adovan² objekt uvede do p∙vodnφho stavu. Dßle je t°eba volat metodu Reset() rozhranφ za°φzenφ s p∙vodnφm nastavenφm za°φzenφ (objekt D3DPRESENT_PARAMETERS).

D∙le₧itΘ je tedy zjiÜ¥ovat, zda-li nedoÜlo ke ztrßt∞ za°φzenφ. ProblΘm je ten, ₧e vlastn∞ vÜechny metody vracφ D3D_OK, i kdy₧ doÜlo ke ztrßt∞. Jedinß metoda za°φzenφ, kterß to odhalφ je TestCooperativeLevel(), kterß vracφ D3DERR_DEVICELOST p°i ztrßt∞ a D3DERR_DEVICENOTRESET v p°φpad∞, ₧e je pot°eba ud∞lat reset za°φzenφ.

Nynφ se podrobn∞ji podφvßme na projekt Display.

22.3. Projekt Display

Jak bychom asi Φekali, tento projekt bude spravovat grafiku zalo₧enou na Direct3D. Po dneÜnφ lekci toho jeÜt∞ p°φliÜ um∞t nebude, ale proberme si t∞ch pßr bod∙:

VÜechny tyto v∞ci jsou pot°ebnΘ a budeme je v dalÜφch lekcφch rozÜi°ovat. Navφc jsem p°idal do inicializace jedno sv∞tlo, aby byly vid∞t kontury objekt∙. NicmΘn∞ sv∞tla si nechßme na p°φÜtφ lekci.

T°φdy knihovny:

Struktury:

22.3.1. XTexture

ZaΦneme p∞kn∞ od spodu. Tento objekt p°edstavuje texturu. Mohli bychom sice p°φmo pou₧φt rozhranφ IDirect3DTexture8, ale minimßln∞ budeme pot°ebovat informace k tomu, abychom mohli texturu kdykoliv zruÜit a znovu obnovit. Textury budeme zatφm vytvß°et z externφch soubor∙, tak₧e z°ejm∞ budeme pot°ebovat cestu k tomuto souboru:

class DISPLAY_API XTexture
{
    LPDIRECT3DTEXTURE8 m_lpTexture;

   
// information for restoring
   
char m_szFilePath[MAX_PATH];
    LPDIRECT3DDEVICE8 m_lpDevice;

public:
    int LoadTextureFromFile(LPCSTR szFileName, LPDIRECT3DDEVICE8 lpDevice);
    int Restore();
    void Release();
    LPDIRECT3DTEXTURE8 GetTexture() { return m_lpTexture; }
    LPDIRECT3DDEVICE8 GetDevice() {return m_lpDevice; }

public:
    XTexture(void);
    ~XTexture(void);
};

Tak₧e krom samotnΘho rozhranφ IDirect3DTexture8, tu mßme ukazatel na za°φzenφ a cestu k souboru bitmapy, ze kterΘ je textura vytvo°ena.

Metody:

  1. LoadTextureFromFile() - Nahrßvanφ textury ze souboru. Soubor bitmapy se hledß v adresß°i Debug nebo Release podle kompilace. Je nutnΘ p°edßvat za°φzenφ, kterΘ si intern∞ ulo₧φme pro obnovu textury.

  2. Restore() - Obnova textury.

  3. Release() - Uvoln∞nφ textury.

  4. GetTexture() - Vracφ ukazatel na rozhranφ textury.

  5. GetDevice() - Vracφ ukazatel na rozhranφ za°φzenφ.

Implementace:

1) Konstruktor a destruktor

XTexture::XTexture(void)
{
    m_lpTexture = NULL;
    m_lpDevice = NULL;
}

XTexture::~XTexture(void)
{
    Release();
    m_lpDevice = NULL;
}

2) Nahrßvßnφ textury ze souboru

int XTexture::LoadTextureFromFile(LPCSTR szFileName, LPDIRECT3DDEVICE8 lpDevice)
{
    if(!lpDevice)
    {
        THROW("LoadTextureFromFile: Device is invalid.");
    }

    if(!m_lpTexture)
    {
        // create full path to the texture
        if(!cmnGetDataFilePath(m_szFilePath, szFileName))
        {
            TRACE("Cannot create full path to the texture.");
        }
        // try to create texture
        if(D3D_OK == D3DXCreateTextureFromFile(lpDevice, m_szFilePath, &m_lpTexture))
        {
            TRACE("Texture '%s' was loaded.", szFileName);
        }
        else
        {
            TRACE("Texture '%s' wasn't loaded.", szFileName);
        }
        // save information to restore
        m_lpDevice = lpDevice;

        return 0;
    }
    return -1;
}


Nejprve otestujeme za°φzenφ, p°φpadn∞ vyhodφme v²jimku. Dßle testujeme zda-li textura ji₧ nenφ vytvo°ena. Vytvo°φme cestu k souboru pomocφ funkce cmnGetDataFilePath() a pokusφme se nahrßt texturu pomocφ funkce D3DXCreateTextureFromFile(). Nakonec ulo₧φme ukazatel na za°φzenφ.

3) Uvoln∞nφ textury

void XTexture::Release()
{
   
// release texture
    SAFE_RELEASE(m_lpTexture);
}


4) Obnova textury

int XTexture::Restore()
{
    // reinit texture
    if(m_lpDevice)
    {
        // try to recreate texture
        if(D3D_OK == D3DXCreateTextureFromFile(m_lpDevice, m_szFilePath, &m_lpTexture))
        {
            TRACE("Texture '%s' was reloaded.", m_szFilePath);
        }
        else
        {
            TRACE("Texture '%s' wasn't reloaded.", m_szFilePath);
        }
    }
    return -1;
}

22.3.2. XMesh

DalÜφ v °ad∞ je objekt XMesh. Nejprve si povφme, co je to vlastn∞ mesh. Objekty v Direct3D jsou sestaveny z polygon∙, nejΦast∞ji z troj·helnφk∙. To ji₧ vφte. Mesh je sφ¥ov² model objektu - Φili informace o tvaru objektu, p°φpadn∞ materißlu Φi textu°e. Informace jsou ulo₧eny v index a vertex bufferech (vφce si o tomto povφme v dalÜφch lekcφch). Podφvejme se na obrßzek:

Toto je drßtov² model konviΦky (teapot). Tento objekt je reprezentovßn rozhranφm ID3DXMesh. Meshe m∙₧ete naΦφtat z externφho souboru s p°φponou .x, p°φpadn∞ ze souboru 3D Studia .3ds. Existuje plugin pro 3D Studio, kter² exportuje model do souboru .x. Tento plugin si m∙₧ete stßhnout v sekci Downloads.

V naÜem enginu budeme prozatφm vytvß°et p°eddefinovanΘ tvary pomocφ knihovny D3DX nebo ze souboru .x. V knihovn∞ D3DX je mnoho funkcφ pro prßci s meshi. Prozatφm budeme vyu₧φvat tyto funkce:

  1. D3DXLoadMeshFromX - naΦte mesh ze souboru .x. Parametry: jmΘno souboru, nastavenφ, za°φzenφ, pole 3 DWORD pro ka₧d² face meshe - urΦuje sousednφ facy, pole materißl∙, poΦet materißl∙, v²sledn² mesh. Dnes nßs budou zajφmat pouze zelenΘ parametry, ostatnφ jsou NULL nebo 0.

  2. D3DXCreateSphere - vytvo°φ kouli o zadanΘm polom∞ru. Parametry: za°φzenφ, polom∞r, poΦet svisl²ch "krajφc∙", poΦet vodorovn²ch "pater", v²sledn² mesh.

  3. D3DXCreateBox - vytvo°φ kvßdr o zadan²ch rozm∞rech. Parametry: za°φzenφ, Üφ°ka, v²Üka, hloubka, v²sledn² mesh.

  4. D3DXCreateCylinder - vytvo°φ vßlec o zadan²ch rozm∞rech. Parametry: za°φzenφ, polom∞r1, polom∞r2, dΘlka, poΦet svisl²ch "krajφc∙", poΦet vodorovn²ch "pater", v²sledn² mesh.

  5. D3DXCreateTeapot - vytvo°φ konviΦku:) Parametry: za°φzenφ, v²sledn² mesh. KonviΦka nemß ₧ßdnΘ dalÜφ parametry.

  6. D3DXCreateTorus - vytvo°φ prstenec (kobliha s dφrou:) Parametry: za°φzenφ, vnit°nφ polom∞r, vn∞jÜφ polom∞r, poΦet stran, poΦet prstenc∙, v²sledn² mesh.

V naÜφ t°φd∞ XMesh musφ b²t op∞t informace, abychom mohli mesh v p°φpad∞ ztrßty za°φzenφ obnovit. U meshe, kter² je nahrßvßn ze souboru  to nenφ problΘm (je to stejnΘ jako u textury). MenÜφ problΘm nastßvß u p°eddefinovan²ch objekt∙. Ka₧d² mß n∞kolik parametr∙, nejvφce jich mß vßlec: 3x float, 2x int. I tyto prom∞nnΘ musφ b²t zahrnuty, ale ne v₧dy se vÜechny vyu₧ijφ. Tφm dochßzφ k mφrnΘmu pl²tvßnφ mφsta a pokud budete pou₧φvat p°evß₧n∞ meshe ze souboru, je lepÜφ ud∞lat zvlßÜtnφ t°φdu. Navφc objekt meshe musφ sßm v∞d∞t co je zaΦ.

T°φda XMesh:

enum MESHTYPE {BOX, CYLINDER, SPHERE, TEAPOT, TORUS, CUSTOM };

class DISPLAY_API XMesh
{
    LPD3DXMESH m_lpMesh;

    // information for restoring
    char m_szFilePath[MAX_PATH];
    LPDIRECT3DDEVICE8 m_lpDevice;
    MESHTYPE m_meshType;
    float m_fPar1, m_fPar2, m_fPar3;
    UINT m_uPar4, m_uPar5;

public:
    int LoadMeshFromFile(LPCSTR szFileName, LPDIRECT3DDEVICE8 lpDevice);
    int CreateSphere(float fRadius, UINT uSlices, UINT uStacks, LPDIRECT3DDEVICE8 lpDevice);
    int CreateBox(float fWidth, float fHeight, float fDepth, LPDIRECT3DDEVICE8 lpDevice);
    int CreateCylinder(float fRadius1, float fRadius2, float fLenght, UINT uSlices, UINT uStacks, LPDIRECT3DDEVICE8 lpDevice);
    int CreateTeapot(LPDIRECT3DDEVICE8 lpDevice);
    int CreateTorus(float fInnerRadius, float fOuterRadius, UINT uSides, UINT uRings, LPDIRECT3DDEVICE8 lpDevice);

    int Restore();
    void Release();
    LPD3DXMESH GetMesh() { return m_lpMesh; }
    LPDIRECT3DDEVICE8 GetDevice() {return m_lpDevice; }

public:
    XMesh(void);
    ~XMesh(void);
};

Metody jsou podobnΘ jako u textury, tak₧e je zde nebudu podrobn∞ rozepisovat. Jen si vÜimn∞te parametr∙ m_fPar1 - m_fPar5. To jsou prßv∞ parametry p°eddefinovan²ch objekt∙. Metody na inicializaci meshe jsem popsal o n∞co v²Üe.

Implementace:

1) Konstruktor a destruktor

XMesh::XMesh(void)
{
    m_lpMesh = NULL;
    m_lpDevice = NULL;
}

XMesh::~XMesh(void)
{
    Release();
    m_lpDevice = NULL;
}


2) Nahrßvßme mesh ze souboru - metoda je podobnß jako LoadTextureFromFile()

int XMesh::LoadMeshFromFile(LPCSTR szFileName, LPDIRECT3DDEVICE8 lpDevice)
{
    if(!lpDevice)
    {
        THROW("LoadMeshFromFile: Device is invalid.");
    }
    if(!m_lpMesh) {

        // create full path to the texture
        if(!cmnGetDataFilePath(m_szFilePath, szFileName))
        {
            TRACE("Cannot create full path to the texture.");
        }
        // load mesh
        if(D3D_OK == D3DXLoadMeshFromX(m_szFilePath, 0, lpDevice, NULL, NULL, NULL, &m_lpMesh))
        {
            TRACE("Mesh '%s' was loaded.", szFileName);
        }
        else
        {
            TRACE("Mesh '%s' wasn't loaded.", szFileName);
        }
        // save par to restore
        m_meshType = CUSTOM;
        m_lpDevice = lpDevice;

        return 0;
    }
    return -1;
}


3) Vytvo°φ mesh ve tvaru koule

int XMesh::CreateSphere(float fRadius, UINT uSlices, UINT uStacks, LPDIRECT3DDEVICE8 lpDevice)
{
    if(!lpDevice)
    {
        THROW("CreateSphere: Device is invalid.");
    }
    if(!m_lpMesh) {

        if(D3D_OK == D3DXCreateSphere(lpDevice, fRadius, uSlices, uStacks, &m_lpMesh, NULL))
        {
            TRACE("Sphere was loaded.");
        }
        else
        {
            TRACE("Sphere wasn't loaded.");
        }
       
// save par to restore
        m_meshType = SPHERE;
        m_lpDevice = lpDevice;
        m_fPar1 = fRadius;
        m_uPar4 = uSlices;
        m_uPar5 = uStacks;

        return 0;
    }
    return -1;
}


4) Vytvo°φ mesh ve tvaru kvßdru

int XMesh::CreateBox(float fWidth, float fHeight, float fDepth, LPDIRECT3DDEVICE8 lpDevice)
{
    if(!lpDevice)
    {
        THROW("CreateBox: Device is invalid.");
    }
    if(!m_lpMesh) {

        if(D3D_OK == D3DXCreateBox(lpDevice, fWidth, fHeight, fDepth, &m_lpMesh, NULL))
        {
            TRACE("Box was loaded.");
        }
        else
        {
            TRACE("Box wasn't loaded.");
        }
       
// save par to restore
        m_meshType = BOX;
        m_lpDevice = lpDevice;
        m_fPar1 = fWidth;
        m_fPar2 = fHeight;
        m_fPar3 = fDepth;

        return 0;
    }
    return -1;
}

5) Vytvo°φ mesh ve tvaru vßlce

int XMesh::CreateCylinder(float fRadius1, float fRadius2, float fLenght, UINT uSlices, UINT uStacks, LPDIRECT3DDEVICE8 lpDevice)
{
    if(!lpDevice)
    {
        THROW("CreateCylinder: Device is invalid.");
    }
    if(!m_lpMesh) {

        if(D3D_OK == D3DXCreateCylinder(lpDevice, fRadius1, fRadius2, fLenght, uSlices, uStacks, &m_lpMesh, NULL))
        {
            TRACE("Cylinder was loaded.");
        }
        else
        {
            TRACE("Cylinder wasn't loaded.");
        }
       
// save par to restore
        m_meshType = CYLINDER;
        m_lpDevice = lpDevice;
        m_fPar1 = fRadius1;
        m_fPar2 = fRadius2;
        m_fPar3 = fLenght;
        m_uPar4 = uSlices;
        m_uPar5 = uStacks;

        return 0;
    }
    return -1;
}

6) Vytvo°φ mesh ve tvaru konviΦky

int XMesh::CreateTeapot(LPDIRECT3DDEVICE8 lpDevice)
{
    if(!lpDevice)
    {
        THROW("CreateTeapot: Device is invalid.");
    }
    if(!m_lpMesh) {

        if(D3D_OK == D3DXCreateTeapot(lpDevice, &m_lpMesh, NULL))
        {
            TRACE("Teapot was loaded.");
        }
        else
        {
            TRACE("Teapot wasn't loaded.");
        }
      
 // save par to restore
        m_meshType = TEAPOT;
        m_lpDevice = lpDevice;

        return 0;
    }
    return -1;
}


7) Vytvo°φ mesh ve tvaru prstence

int XMesh::CreateTorus(float fInnerRadius, float fOuterRadius, UINT uSides, UINT uRings, LPDIRECT3DDEVICE8 lpDevice)
{
    if(!lpDevice)
    {
        THROW("CreateTorus: Device is invalid.");
    }
    if(!m_lpMesh) {

        if(D3D_OK == D3DXCreateTorus(lpDevice, fInnerRadius, fOuterRadius, uSides, uRings, &m_lpMesh, NULL))
        {
            TRACE("Torus was loaded.");
        }   
        else
        {
            TRACE("Torus wasn't loaded.");
        }
       
// save par to restore
        m_meshType = TORUS;
        m_lpDevice = lpDevice;
        m_fPar1 = fInnerRadius;
        m_fPar2 = fOuterRadius;
        m_uPar4 = uSides;
        m_uPar5 = uRings;
        return 0;
    }
    return -1;
}


8) Obnovφ mesh podle typu

int XMesh::Restore()
{
    if(m_lpDevice)
    {
        switch(m_meshType) {
            case CUSTOM:
              
 // load mesh
                if(D3D_OK == D3DXLoadMeshFromX(m_szFilePath, 0, m_lpDevice, NULL, NULL, NULL, &m_lpMesh))
                {
                    TRACE("Mesh '%s' was loaded.", m_szFilePath);
                    return 0;
                }
                else
                {
                    TRACE("Mesh '%s' wasn't loaded.", m_szFilePath);
                }
                break;
            case SPHERE:
                return CreateSphere(m_fPar1, m_uPar4, m_uPar5, m_lpDevice);
            case BOX:
                return CreateBox(m_fPar1, m_fPar2, m_fPar3, m_lpDevice);
            case CYLINDER:
                return CreateCylinder(m_fPar1, m_fPar2, m_fPar3, m_uPar4, m_uPar5, m_lpDevice);
            case TORUS:
                return CreateTorus(m_fPar1, m_fPar2, m_uPar4, m_uPar5, m_lpDevice);
            case TEAPOT:
                return CreateTeapot(m_lpDevice);
        }
    }
    return -1;
}


9) Uvolnφ rozhranφ meshe

void XMesh::Release()
{
    SAFE_RELEASE(m_lpMesh);
}

VÜimn∞te si, ₧e metody vyu₧φvajφ funkce a makra z knihovny common.dll, tak₧e nezapome≥te vlo₧it soubor common.h.

22.3.3. XResourceManager

Jak jsme si u₧ pov∞d∞li, objekty typu mesh nebo textury je t°eba obnovit po resetu za°φzenφ. Abychom m∞li p°ehled o t∞chto objektech, vytvo°φme jak²si manager t∞chto objekt∙: XResourceManager, kter² bude udr₧ovat seznam vÜech pou₧φvan²ch textur a mesh∙. Tak₧e budeme pot°ebovat n∞jakΘ dynamickΘ pole, do kterΘho budeme uklßdat ukazatele na tyto objekty. Dßle tyto objekty budeme vklßdat (pozd∞ji r∙zn∞ upravovat) a takΘ budeme chtφt vÜechny uvolnit a potΘ obnovit.

NßÜ resource manager bude pro zaΦßtek ·pln∞ jednoduch²:

#include <vector>

class DISPLAY_API XResourceManager
{
    std::vector <XTexture*> m_arTextures;
    std::vector <XMesh*> m_arMeshes;

public:
    int AddTexture(XTexture * pTextureToAdd);
    int AddMesh(XMesh * pMeshToAdd);
    int ReleaseAll();
    int RestoreAll();

public:
    XResourceManager(void);
    ~XResourceManager(void);
};

Mßme zatφm textury a meshe, tak₧e pro n∞ ud∞lßme dynamickΘ pole. K tomu pou₧ijeme objekt knihovny STL vector. Dßle tu mßme metody pro ulo₧enφ ukazatele na texturu a meshe, metody pro uvoln∞nφ vÜech objekt∙ a jejich obnovenφ.

Implementace:

1) Konstruktor a destruktor

XResourceManager::XResourceManager(void)
{

}

XResourceManager::~XResourceManager(void)
{
    ReleaseAll();
    m_arTextures.clear();
    m_arMeshes.clear();
}

2) Vlo₧φ ukazatel na texturu - tφm texturu "zaregistrujete" a u₧ se o nφ nemusφte v∙bec starat

int XResourceManager::AddTexture(XTexture * pTextureToAdd)
{
    if(pTextureToAdd) {
        m_arTextures.push_back(pTextureToAdd);
        return 0;
    }
    return -1;
}

3) Vlo₧φ ukazatel na mesh  - tφm mesh "zaregistrujete" a u₧ se o n∞j nemusφte v∙bec starat

int XResourceManager::AddMesh(XMesh * pMeshToAdd)
{
    if(pMeshToAdd) {
        m_arMeshes.push_back(pMeshToAdd);
        return 0;
    }
    return -1;
}


4) Uvolnφ vÜechny objekty - textury i meshe

int XResourceManager::ReleaseAll()
{
   
// release textures
    for(int i = 0; i < (int)m_arTextures.size(); i++) {
        m_arTextures[i]->Release();
    }
    for(int i = 0; i < (int)m_arMeshes.size(); i++) {
        m_arMeshes[i]->Release();
    }
    return 0;
}


5) Obnovφ vÜechny objekty

int XResourceManager::RestoreAll()
{
   
// restore textures
    for(int i = 0; i < (int)m_arTextures.size(); i++) {
        m_arTextures[i]->Restore();
    }
    for(int i = 0; i < (int)m_arMeshes.size(); i++) {
        m_arMeshes[i]->Restore();
    }
    return 0;
}

Jist∞ jste si vÜimli, ₧e prozatφm vÜechny metody vraceli hodnotu 0 v p°φpad∞ ·sp∞chu, jinak hodnotu -1.

Z knihovny Display u₧ nßm zb²vß pouze t°φda XDisplay.

22.3.4. XDisplay

Tato t°φda je prakticky nejd∙le₧it∞jÜφ a proto si ji rozebereme na zßv∞r. Pracuje toti₧ s p°edchozφmi objekty. Hlavnφ ·kol tΘto t°φdy je sprßvn∞ zinicializovat systΘm Direct3D, vyΦistit pozadφ, prohozenφ buffer∙ a dalÜφ v∞ci, kterΘ budeme teprve Φasem dopl≥ovat.

Atributy t°φdy m∙₧eme rozd∞lit nßsledovn∞:

Poznßmka: RozliÜenφ uklßdßme do samostatnΘ struktury Resolution, kterß pouze obsahuje velikost rozliÜenφ v obou sm∞rech.

T°φda XDisplay v celΘ svΘ krßse:

class DISPLAY_API XDisplay
{
    // D3D objects
    IDirect3D8 * m_lpD3DObject;
    IDirect3DDevice8* m_lpD3DDevice;
    D3DPRESENT_PARAMETERS m_d3dDeviceParam;
    XResourceManager m_resManager;
    //
    // Display settings
    WORD m_wFlags;
    // Display parameters
    Resolution m_sRes;
    UINT m_iDepth;
    D3DDEVTYPE m_typeDeviceType;
    int m_iAdapterOrdinal;
    D3DFORMAT m_formatScreenDepth;
    D3DFORMAT m_formatTextureDepth;
    D3DTEXTUREFILTERTYPE m_tftMagTextureFilter;
    D3DTEXTUREFILTERTYPE m_tftMinTextureFilter;
    D3DTEXTUREFILTERTYPE m_tftMipTextureFilter;
    //
    // FPS atributes
 
   float m_fStartTime;
    float m_fStopTime;
    FLOAT m_Frames;
    float m_fCurrentFPS;
    LPD3DXFONT m_lpFPSFont;
    float m_fFrameRate;
    int m_iDesiredFPS;
 
   //
    // Time between two frames
 
   float m_fTime;
    // Time from app start
    float m_fTotalTime;
    // Background color
    D3DCOLOR m_dwBackgroundColor;
    // Temporary var.
    D3DXMATRIX *m_matWorld;
    D3DLIGHT8 Light;

private:
    int UpdateFPS();
    void LimitFPS();
    void Clean(void);
    void BuildUpMatrices(D3DXVECTOR3 *pvEyePt, D3DXVECTOR3 *pvLookAtPt);

public:
    int Init(HWND hWnd);
    int UpdateBackground();
    int Present();
    int RestoreDisplay();

    LPDIRECT3DDEVICE8 GetDevice() {return m_lpD3DDevice;}
    XResourceManager* GetResourceManager() {return &m_resManager; }

public:
    XDisplay(void);
    ~XDisplay(void);
};

VÜimn∞te si, ₧e vÜechny t°φdy jsme doposud exportovali z knihovny. Atributy si podrobn∞ji popφÜeme a₧ u implementace metod.

Metody:

Implementace:

1) Konstruktor, destruktor a Φistφcφ funkce Clean()

XDisplay::XDisplay(void)
{
    m_lpD3DObject = NULL;
    m_lpD3DDevice = NULL;
    m_matWorld = new D3DXMATRIX;
}

XDisplay::~XDisplay(void)
{
    Clean();
    delete m_matWorld;
}

void XDisplay::Clean(void)
{
    if(m_lpD3DDevice) {

        m_lpD3DDevice->SetTexture(0, NULL);

        m_resManager.ReleaseAll();

        SAFE_RELEASE(m_lpD3DDevice);
        SAFE_RELEASE(m_lpD3DObject);
    }
}

Na zaΦßtku musφme vynulovat ukazatel na za°φzenφ, abychom poznali, zda-li je t°φda ji₧ zinicializovßna. Naopak p°i uvoln∞nφ musφme vyjmout texturu "ze za°φzenφ", aby tato textura Üla uvolnit. Dßle uvolnφme vÜechny zdroje a nakonec objekty Direct3D.

2) Inicializace systΘmu

int XDisplay::Init(HWND hWnd)
{
    TRACE("Initializing Direct3D system...");
    HRESULT dwRet;
    DWORD BehaviorFlags;
    //
    // Already initialized?
    if(m_lpD3DDevice) {
        THROW("Direct3D system is already initialized.");
    }
    //
    // As first, create one Direct3D object
    m_lpD3DObject = Direct3DCreate8(D3D_SDK_VERSION);
    if(!m_lpD3DObject) {
        cmnMessageBoxA("Please, install DirectX 8.1.", MSG_ERROR);
        THROW("Cannot create object of Direct3D.");
    }
 
   //
    //
    // Read desired display format from ini file
    //
    // Get resolution values from setup.ini file
    m_sRes.cx = cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_WIDTH);
    m_sRes.cy = cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_HEIGHT);
 
  //
    // Get screen depth from ini file (may be 16 or 32)
 
   m_formatScreenDepth = (D3DFORMAT)cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_FORMAT);
 
   //
    // If system fullsreen or windowed
 
   if(cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_WINDOWED)) {
        m_wFlags |= FLAG_WINDOWED;
    }
 
   //
    // Is FPS enabled? Default is disabled.
 
   if(cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_FPS)) {
        m_wFlags |= FLAG_FPS;
    }
 
   //
    // Get desired FPS from ini. Default is off.
    // If m_iDesiredFPS == 0, then FPS limiter is off.
    m_iDesiredFPS = cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_DESIREDFPS);
 
   //
    // Read type of vertex processing
    BehaviorFlags = cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_BEHAVIOR);
    //
    // Read texture format
    m_formatTextureDepth = (D3DFORMAT)cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_TEXDEPTH);
 
   //
    // Set size of back buffer from window position
 
   if(m_wFlags & FLAG_WINDOWED) {
        D3DDISPLAYMODE displaymode;
        RECT rcWindowClient;
        //
        // Get window position
        GetClientRect(hWnd, &rcWindowClient);
        // Get current display mode
        dwRet = m_lpD3DObject->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displaymode);
        if(dwRet != D3D_OK) {
            THROWERR("Cannot get display format in window mode due ", dwRet);
        }
        //
        // Set size of back buffer
        m_sRes.cx = rcWindowClient.right - rcWindowClient.left;
        m_sRes.cy = rcWindowClient.bottom - rcWindowClient.top;
        // Set display mode to current
        m_formatScreenDepth = displaymode.Format;
    }
    //
    // Read ordinal number of used adapter
    m_iAdapterOrdinal = cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_ADAPTER);
    // Read type of device on this adapter
    m_typeDeviceType = (D3DDEVTYPE) cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_DEVICE);
    m_tftMagTextureFilter = (D3DTEXTUREFILTERTYPE) cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_MAGTEXTUREFILTER);
    m_tftMinTextureFilter = (D3DTEXTUREFILTERTYPE) cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_MINTEXTUREFILTER);
    m_tftMipTextureFilter = (D3DTEXTUREFILTERTYPE) cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_MIPTEXTUREFILTER);
    //
    // Set properties of device
    ZeroMemory(&m_d3dDeviceParam, sizeof(m_d3dDeviceParam));
    m_d3dDeviceParam.Windowed = (m_wFlags & FLAG_WINDOWED) ? TRUE : FALSE;
    m_d3dDeviceParam.SwapEffect = D3DSWAPEFFECT_DISCARD;
    m_d3dDeviceParam.BackBufferWidth = m_sRes.cx;
    m_d3dDeviceParam.BackBufferHeight = m_sRes.cy;
    m_d3dDeviceParam.BackBufferFormat = m_formatScreenDepth;
    m_d3dDeviceParam.BackBufferCount = 1;
    m_d3dDeviceParam.hDeviceWindow = hWnd;
    m_d3dDeviceParam.Flags = 0;
    //
    // -> Fullscreen AA
    m_d3dDeviceParam.MultiSampleType = (D3DMULTISAMPLE_TYPE)cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_FSAA);
    m_d3dDeviceParam.EnableAutoDepthStencil = TRUE;
    m_d3dDeviceParam.AutoDepthStencilFormat = D3DFMT_D16;
    //
    // Set some settings for fullscreen mode
    // -> PresentationInterval
    // -> RefreshRate
    if(!(m_wFlags & FLAG_WINDOWED)) {
        //
        // -> PresentationInterval
        if(cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_VSYNC) == 0) {
            m_d3dDeviceParam.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
        }
        else {
            m_d3dDeviceParam.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_ONE;
        }
        //
        // -> RefreshRate
        m_d3dDeviceParam.FullScreen_RefreshRateInHz = cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_REFRESHRATE);
    }
    // Now we can create D3D device
    dwRet = m_lpD3DObject->CreateDevice(m_iAdapterOrdinal, m_typeDeviceType, hWnd,
                                        BehaviorFlags, &m_d3dDeviceParam, &m_lpD3DDevice);
    if(dwRet != D3D_OK) {
        cmnMessageBoxA("Please, run Setup to set display settings.", MSG_ERROR);
        Clean();
        THROWERR("Cannot create D3D device due ", dwRet);
    }
    //
    // Set texture filter for first stage
    m_lpD3DDevice->SetTextureStageState(0, D3DTSS_MIPFILTER, m_tftMipTextureFilter);
    m_lpD3DDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, m_tftMagTextureFilter);
    m_lpD3DDevice->SetTextureStageState(0, D3DTSS_MINFILTER, m_tftMinTextureFilter);

    m_dwBackgroundColor = 56564;

    BuildUpMatrices(&D3DXVECTOR3(5.0f, 5.0f, 5.0f), &D3DXVECTOR3(0.0f, 0.0f, 0.0f));

    return 0;
}

Tato funkce naΦte vÜechny mo₧nΘ informace ze souboru setup.ini. V∞tÜina t∞chto dat je pro nßs zatφm ned∙le₧it²ch, ale postupn∞ je zaΦneme vyu₧φvat. Nejprve vytvo°φme objekt Direct3D a potΘ postupn∞ naΦφtßme tyto data:

Dßle musφme nastavit n∞jakΘ specißlnφ vlastnosti, pokud aplikace b∞₧φ v okn∞. Hlavnφ je velikost zadnφho bufferu, kterou spoΦφtßme podle velikosti okna. Dßle je t°eba zφskat aktußlnφ formßt pou₧it²  Windows.

V dalÜφm kroku ji₧ nastavφme vlastnosti za°φzenφ velice podobn∞ jako v minulΘ lekci a vytvo°φme za°φzenφ. V poslednφm kroku nastavφme matice pohledu a projekce pomocφ metody BuildUpMatrices(). N∞kterΘ atributy nastavujeme jen p°i fullscreen re₧imu (nap°. vertikßlnφ synchronizaci nebo obnovovacφ frekvenci).

3) Nastavenφ matic a vytvo°enφ sv∞tla

void XDisplay::BuildUpMatrices(D3DXVECTOR3 *pvEyePt, D3DXVECTOR3 *pvLookAtPt)
{
    D3DXMATRIX matWorld, matView, matProj;
    D3DXVECTOR3 vUpDir;
    // Check parameters
    if(!pvEyePt || !pvLookAtPt) {
        THROW("BuildUpMatrices: Invalid input vectors.");
    }
 
   //
    // Vertical axis is Z
 
   vUpDir = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
    //
    // Set transformations
    D3DXMatrixIdentity(m_matWorld);
    //
    // Create view and projection matrices
    D3DXMatrixLookAtLH(&matView, pvEyePt, pvLookAtPt, &vUpDir);
    D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, 4.0f/3.0f, 1.0f, 50.0f);
    //
    // Set matrices
    m_lpD3DDevice->SetTransform(D3DTS_WORLD, m_matWorld);
    m_lpD3DDevice->SetTransform(D3DTS_VIEW, &matView);
    m_lpD3DDevice->SetTransform(D3DTS_PROJECTION, &matProj);
 
   //
    // Enable Z-buffer
 
   m_lpD3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
    //
    // Set wireframe, if user wants to render wireframe model
    if(cmnReadSetupInt(_S_SECTION_DISPLAY, _S_KEY_WIREFRAME)) {
        m_lpD3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
        m_lpD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
    }
   
    D3DLIGHT8 Light;
    ZeroMemory( &Light, sizeof(D3DLIGHT8) );
    Light.Type = D3DLIGHT_DIRECTIONAL;
    Light.Direction.x = -1.0f;
    Light.Direction.y = 0.0f;
    Light.Direction.z = -1.0f;
    Light.Diffuse.r = 0.7f;
    Light.Diffuse.g = 0.7f;
    Light.Diffuse.b = 0.0f;
    Light.Ambient.r = 0.5f;
    Light.Ambient.g = 0.5f;
    Light.Ambient.b = 0.5f;
    Light.Range = 1000.0f;
   
    D3DMATERIAL8 mtrl;
    ZeroMemory( &mtrl, sizeof(D3DMATERIAL8) );
    mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f;
    mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f;
    mtrl.Diffuse.b = mtrl.Ambient.b = 1.0f;
    mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f;
    m_lpD3DDevice->SetMaterial(&mtrl);
   
    m_lpD3DDevice->SetLight(0, &Light);
    m_lpD3DDevice->LightEnable(0, TRUE);
    m_lpD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
}

Prvnφ Φßst funkce je celkem srozumitelnß, proto₧e podobnΘ nastavenφ jsme d∞lali i v minulΘ lekci. Dßle ale musφme zapnout Z-buffer a v p°φpad∞, ₧e u₧ivatel chce drßtov² model i Wireframe. O Z-bufferu si povφme v n∞kterΘ p°φÜtφ lekci. V poslednφ Φßsti vytvo°φme a nastavφme sv∞tlo a materißl. O tomto jsme si zatφm takΘ nic nepov∞d∞li, ale do₧eneme to p°φÜt∞. Tuto funkci je t°eba volat i po resetu za°φzenφ, proto₧e p°i za°φzenφ ztratφ vÜechny stavy, matice atd.

4) VyΦiÜt∞nφ zadnφho bufferu a Z-buffer

int XDisplay::UpdateBackground()
{
    if(m_lpD3DDevice) {
     
  //
        // Clear the back buffer to a blue color
      
 m_lpD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET| D3DCLEAR_ZBUFFER, m_dwBackgroundColor, 1.0f, 0);

        D3DXMATRIX matWorld, matRot;
        float fFactor = m_fTime;
        if(fFactor != 0) {
            D3DXMatrixRotationX(&matRot, 0.9f * fFactor);
            D3DXMatrixMultiply(m_matWorld, m_matWorld, &matRot);
            m_lpD3DDevice->SetTransform(D3DTS_WORLD, m_matWorld);
        }
        return 0;
    }
    return 1;
}

Zde pouze nastavφme po₧adovanou barvu pozadφ a vyΦistφme Z-Buffer (viz. minulß lekce). V druhΘ Φßsti transformujeme sv∞tovou matici a tφm zp∙sobφme rotaci scΘny.

5) Prohozenφ buffer∙

int XDisplay::Present()
{
    DWORD dwRet;
    if(m_lpD3DDevice) {
        // Get time
        m_fTime = cmnGetTime(TIMER_GETELAPSEDTIME);
        m_fTotalTime += m_fTime;
       
        // Test the cooperative level to see if it's okay to render
        dwRet = m_lpD3DDevice->TestCooperativeLevel();
        if(dwRet != D3D_OK)
        {
            // If the device was lost, do not render until we get it back
            if( D3DERR_DEVICELOST == dwRet ) {
           
            }
            // Check if the device needs to be resized.
            if( D3DERR_DEVICENOTRESET == dwRet ) {
                RestoreDisplay();
            }
            return (int)dwRet;
        }

        if(m_wFlags & FLAG_FPS) {
            UpdateFPS();
        }
        // Set limitations
        if(m_iDesiredFPS != 0) {
            LimitFPS();
        }

        m_lpD3DDevice->Present(NULL, NULL, NULL, NULL);
    }
    return 0;
}

Zde postupn∞ urΦφme Φas od poslednφho volßnφ metody, tento Φas p°ipoΦteme k celkovΘmu Φasu. Dßle testujeme zda-li nenφ za°φzenφ ztraceno. Pokud k tomu dojde, p°estaneme prohazovat povrchy a Φekßme a₧ bude pot°eba za°φzenφ obnovit. PotΘ volßme metodu RestoreDisplay(). V normßlnφm chodu zobrazujeme a omezujeme FPS. Nakonec provedeme present.

6) Zobrazenφ a omezeni FPS

int XDisplay::UpdateFPS()
{
    //
    // Get time of system start
    m_fStartTime = cmnGetTime(TIMER_GETAPPTIME);
 
   //
    // Increment frames counter
 
   m_Frames++;
 
   //
    // Compute number of frames per one second
 
   if((m_fStartTime - m_fStopTime) >= FPS_REFRESH_RATE) {

        m_fCurrentFPS = m_Frames / (m_fStartTime - m_fStopTime);
        m_Frames = 0;
   
    //
        // Save old-new time
   
    m_fStopTime = m_fStartTime;
        TRACE("%4.1f FPS - ElapsedT: %4.8f: %d", m_fCurrentFPS, m_fTime);
    }
   
//
    return 1;
}

void XDisplay::LimitFPS()
{
    static float last = 0;
    do
    {
        float now = cmnGetTime(TIMER_GETAPPTIME);
        float passed = now - last;
        m_fFrameRate = float(1.0 / float(passed));
    }
    while(m_fFrameRate > m_iDesiredFPS);
    last = cmnGetTime(TIMER_GETAPPTIME);
}

V metod∞ UpdateFPS() musφme spoΦφtat snφmky za vte°inu. U₧ jsme podobnou funkci psali pro DirectDraw. Po jisty Φasov² okam₧ik poΦφtßme poΦet cykl∙, pak toto Φφslo p°evedeme tak, aby odpovφdalo 1 vte°in∞. V metod∞ LimitFPS() je cyklus, ve kterΘm spoΦφtßme aktußlnφ framerate. Cyklus ukonΦφme, pokud je tento framerate alespo≥ tak velk² jako po₧adovanΘ FPS.

7) Obnovenφ za°φzenφ

int XDisplay::RestoreDisplay()
{
    TRACE("Restoring device...");
 
   //
    // Release all resources
 
   if(!m_resManager.ReleaseAll())
    {
        TRACE("Resources released.");
    }
 
   //
    // Reset the device
    if(D3D_OK != m_lpD3DDevice->Reset(&m_d3dDeviceParam))
    {
        TRACE("Cannot RESET device object.");
    }
    else {
        TRACE("Device is reset.");
    }
    // Wait until device is reset
    Sleep(150);

    // restore world/view/proj matrices and set render stages
    BuildUpMatrices(&D3DXVECTOR3(5.0f, 5.0f, 5.0f), &D3DXVECTOR3(0.0f, 0.0f, 0.0f));

    // reinit resources
    m_resManager.RestoreAll();

    return 0;
}

Tato metoda provede ty t°i kroky, kterΘ jsem uvßd∞l v Φßsti 22.2.:

  1. Uvolnφ vÜechny zdroje - textury, meshe, shadery, index a vertex buffery.

  2. Resetuje za°φzenφ a chvilku poΦkß.

  3. Obnovφ vÜechny zdroje tj. znovu naΦte vÜechny textury, meshe atd.

Zde prßv∞ pou₧ijeme nßÜ resource manager, kter² vφ o vÜech alokovan²ch zdrojφch.

22.4. Testovacφ aplikace

Na zßv∞r lekce jeÜt∞ vyzkouÜφme vÜechny novΘ funkce knihovny Display. K tomu ·Φelu vlo₧φme do projektu Win32 aplikaci Tester. Zde zinicializujeme systΘm Direct3D a vytvo°φme pßr mesh∙: koule, kvßdr, vßlec, konviΦka, prstenec a mesh ze souboru tiger.x. Na model tygra je dßle t°eba naΦφst texturu tiger.bmp.

Zde jsou pou₧itΘ objekty:

XDisplay g_theDisplay;

XTexture g_Tex;
XMesh g_Sphere;
XMesh g_Box;
XMesh g_Cylinder;
XMesh g_Torus;
XMesh g_Teapot;
XMesh g_Tiger;

K inicializace XDisplay je t°eba znßt handle okno:

HWND g_hWnd;

JednotlivΘ meshe p∙jdou vypnout klßvesami F1-F6. Informaci o viditelnosti meshe je ulo₧ena v poli:

BOOL g_VisObj[6];

P°idßme jedinou funkci na obnovenφ snφmku:

void UpdateFrame();

Do funkce WinMain() p°idßme nßsledujφcφ k≤d:

// inicializace logovaciho souboru, pokud je zapnuta funkce Tracetofile
if(cmnReadSetupInt(_S_DEBUG, _S_TRACETOFILE) == 1) {
    cmnInitLog("log.txt", FALSE);
}

// inicializace Direct3D
g_theDisplay.Init(g_hWnd);

// textura tygra
g_Tex.LoadTextureFromFile("tiger.bmp", g_theDisplay.GetDevice());

// preddefinovane objekty
g_Sphere.CreateSphere(2.0f, 48, 48, g_theDisplay.GetDevice());
g_Box.CreateBox(1.0f, 1.0f, 6.0f, g_theDisplay.GetDevice());
g_Cylinder.CreateCylinder(1.5f, 2.5f, 4.0f, 24, 8, g_theDisplay.GetDevice());
g_Torus.CreateTorus(1.0f, 2.0f, 24, 24, g_theDisplay.GetDevice());
g_Teapot.CreateTeapot(g_theDisplay.GetDevice());
// model tygra
g_Tiger.LoadMeshFromFile("tiger.x", g_theDisplay.GetDevice());

// zaregistrovani vsech zdroju
g_theDisplay.GetResourceManager()->AddMesh(&g_Tiger);
g_theDisplay.GetResourceManager()->AddMesh(&g_Sphere);
g_theDisplay.GetResourceManager()->AddMesh(&g_Box);
g_theDisplay.GetResourceManager()->AddMesh(&g_Cylinder);
g_theDisplay.GetResourceManager()->AddMesh(&g_Teapot);
g_theDisplay.GetResourceManager()->AddMesh(&g_Torus);
g_theDisplay.GetResourceManager()->AddTexture(&g_Tex);

// po spusteni je pouze druhy objekt viditelny
g_VisObj[0] = 0;
g_VisObj[1] = 1;
g_VisObj[2] = 0;
g_VisObj[3] = 0;
g_VisObj[4] = 0;
g_VisObj[5] = 0;


// Main message loop:
while( TRUE )
{
  
 // Look for messages, if none are found then
    // update the state and display it

    if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
    {
        if( 0 == GetMessage(&msg, NULL, 0, 0 ) )
        {
           
// WM_QUIT was posted, so exit
            return (int)msg.wParam;
        }

       
// Translate and dispatch the message
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    else
    {
        // Obnova snimku
        UpdateFrame();
    }
}
return (int) msg.wParam;


K tomuto snad nenφ co dodat. VÜechny pot°ebnΘ informace najdete v  komentß°φch. Nynφ trochu upravφme funkci WndProc(), proto₧e chceme vyu₧it klßvesy F1-F6 na ovlßdßnφ model∙ a klßvesu F7 na "um∞l²" reset za°φzenφ, aby bylo z°ejmΘ, ₧e vÜechno funguje tak jak mß. Do p°φkazu switch p°idejte tuto v∞tev:

case WM_KEYUP:
    switch( wParam )
    {
        case VK_F7:
            g_theDisplay.RestoreDisplay();
            break;
        case VK_F1:
            g_VisObj[0] = !g_VisObj[0];
            break;
        case VK_F2:
            g_VisObj[1] = !g_VisObj[1];
            break;
        case VK_F3:
            g_VisObj[2] = !g_VisObj[2];
            break;
        case VK_F4:
            g_VisObj[3] = !g_VisObj[3];
            break;
        case VK_F5:
            g_VisObj[4] = !g_VisObj[4];
            break;
        case VK_F6:
            g_VisObj[5] = !g_VisObj[5];
            break;
        case VK_ESCAPE:
            PostQuitMessage(0);
            break;
    }
    break;

Jako poslednφ uvedu funkci UpdateFrame(), kterß provede vyΦiÜt∞nφ pozadφ, vykreslenφ p°φsluÜn²ch model∙ a present.

void UpdateFrame()
{
    g_theDisplay.UpdateBackground();
    g_theDisplay.GetDevice()->BeginScene();

    if(g_VisObj[0]) {
        if(g_Sphere.GetMesh())
            g_Sphere.GetMesh()->DrawSubset(0);
    }

    if(g_VisObj[1]) {
        if(g_Torus.GetMesh())
            g_Torus.GetMesh()->DrawSubset(0);
    }

    if(g_VisObj[2]) {
        if(g_Box.GetMesh())
            g_Box.GetMesh()->DrawSubset(0);
    }

    if(g_VisObj[3]) {
        if(g_Cylinder.GetMesh())
            g_Cylinder.GetMesh()->DrawSubset(0);
    }

    if(g_VisObj[4]) {
        if(g_Teapot.GetMesh())
            g_Teapot.GetMesh()->DrawSubset(0);
    }

    if(g_VisObj[5]) {
        g_theDisplay.GetDevice()->SetTexture(0, g_Tex.GetTexture());
        if(g_Tiger.GetMesh())
            g_Tiger.GetMesh()->DrawSubset(0);
        g_theDisplay.GetDevice()->SetTexture(0, NULL);
    }

    g_theDisplay.GetDevice()->EndScene();
    g_theDisplay.Present();
}

Zde je t°eba volat metody BeginScene() a EndScene(). Dßle si n∞co povφme o vykreslovanφ meshe, kterΘ je zde znaΦn∞ zjednoduÜeno a postupn∞ ho vylepÜφme. Obecn∞ se toti₧ mesh sklßdß z n∞kolika tzv. subset∙, co₧ je vlastn∞ pod-mesh danΘho meshe, kter² mß sv∙j materißl a texturu. V naÜem jednoduchΘm p°φpad∞ mß ka₧d² objekt pouze jeden subset, tudφ₧ ho m∙₧eme vykreslit pomocφ metody DrawSubset() s parametrem 0. Pro objekt tygra je t°eba nastavit texturu tiger.bmp. Tento zp∙sob vykreslovanφ mesh∙ nenφ vhodn² a p°φÜt∞ vytvo°φme metodu XMesh::Draw(), kterß se bude starat o vÜe, vΦetn∞ nastavenφ textury a materißlu.

22.5. Zßv∞r

Vidφte, ₧e cel² projekt, je docela komplikovan², tak₧e doporuΦuji pou₧φt ukßzku z ChipCD, kterou naleznete v sekci Downloads. Pomocφ klßves F1-F6 m∙₧ete libovoln∞ zobrazovat nahranΘ modely. Pomocφ klßvesy F7 vynutφte um∞l² reset za°φzenφ. Klßvesou escape ukonΦφte aplikaci.

V dneÜnφ lekci jsem splnil, co jsem slφbil, ale nedov∞d∞li jste se prakticky nic novΘho! P°φÜt∞ budeme dßle budovat nßÜ engine a podφvßme se podrobn∞ji na n∞kterΘ dalÜφ vlastnosti. TakΘ bych cht∞l probrat sv∞tla, tak₧e p°idßme podporu sv∞tel.

T∞Üφm se p°φÜt∞ nashledanou.

Ji°φ Formßnek