DirectX (24.)

V dneÜnφ lekci p°idßme podporu vertex a index buffer∙ do naÜeho enginu. V druhΘ Φßsti si povφme n∞co o technice mipmap, tak₧e dßle rozÜφ°φme t°φdu XTexture z p°edminulΘ lekce.

24.1. Podpora Vertex a Index buffer∙

Tyto buffery jsou podobn∞ jako textury pova₧ovßny za zdroje, tak₧e je nutnΘ je p°i ztrßt∞ za°φzenφ smazat, znovu vytvo°it a naplnit. Zde nastßvß problΘm jak uchovat data z bufferu. Mohli bychom vytvo°it kopii t∞chto dat a z tΘto kopie obnovit obsah, ale tento zp∙sob by byl znaΦn∞ pam∞¥ov∞ neefektivnφ, nebo¥ bychom data m∞li v pam∞ti dvakrßt! Na druhou stranu bychom se o obnovu bufferu nemuseli v∙bec starat. ProblΘm m∙₧eme vy°eÜit tak, ₧e po obnovenφ buffer∙ (tj. po volßnφ funkce RestoreBuffer()) znovu naplnφme buffer, tak₧e o obsah se bude starat klientskß aplikace (v naÜem p°φkladu program Tester).  U dynamick²ch buffer∙ tento problΘm v∙bec nenastßvß, proto₧e tyto buffery jsou pln∞ny v ka₧dΘm cyklu aplikace, tak₧e staΦφ pouze znovu vytvo°it buffer.

Do projektu Display p°idßme dv∞ novΘ t°φdy:
- XVertexBuffer
- XIndexBuffer

Ob∞ tyto t°φdy budou mφt velice podobnΘ metody:
- Create()
- Release()
- Restore()
- GetBuffer()
- GetDevice()

VÜimn∞te si, ₧e metody jsou velice podobnΘ t∞m z t°φdy XTexture. Ob∞ t°φdy se budou liÜit jen v n∞kolika parametrech metody Create().

Deklarace t°φd:

class XVertexBuffer
{
    UINT m_uiLength;
    DWORD m_dwUsage;
    DWORD m_dwFVF;
    D3DPOOL m_d3dPool;
    IDirect3DVertexBuffer8* m_pVertexBuffer;
    IDirect3DDevice8* m_lpDevice;

public:
    int Create(UINT uiLength, DWORD dwUsage, DWORD dwFVF, D3DPOOL dwPool, LPDIRECT3DDEVICE8 lpDevice);
    int Restore();
    void Release();
    LPDIRECT3DVERTEXBUFFER8 GetBuffer() { return m_pVertexBuffer; }
    LPDIRECT3DDEVICE8 GetDevice() {return m_lpDevice; }

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

class XIndexBuffer
{
    UINT m_uiLength;
    DWORD m_dwUsage;
    D3DFORMAT m_dwFormat;
    D3DPOOL m_d3dPool;
    IDirect3DIndexBuffer8* m_pIndexBuffer;
    IDirect3DDevice8* m_lpDevice;

public:
    int Create(UINT uiLength, DWORD dwUsage, D3DFORMAT dwFormat, D3DPOOL dwPool, LPDIRECT3DDEVICE8 lpDevice);
    int Restore();
    void Release();
    LPDIRECT3DINDEXBUFFER8 GetBuffer() { return m_pIndexBuffer; }
    LPDIRECT3DDEVICE8 GetDevice() {return m_lpDevice; }

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


Z minul²ch lekcφ ji₧ vφme jakΘ parametry majφ tyto buffery. VÜechny tyto parametry musφme ulo₧il ve t°φd∞, abychom mohli buffer obnovit. Uklßdßme takΘ ukazatel na za°φzenφ a samotn² buffer. Implementace metod bude velice podobnß jako u t°φdy XTexture. Uvedu zde pouze implementaci metod t°φdy XVertexBuffer, proto₧e implementace t°φdy XIndexBuffer je obdobnß.

XVertexBuffer::XVertexBuffer(void)
{
    m_lpDevice = NULL;
    m_pVertexBuffer = NULL;
}

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

int XVertexBuffer::Create(UINT uiLength, DWORD dwUsage, DWORD dwFVF, D3DPOOL dwPool, LPDIRECT3DDEVICE8 lpDevice)
{
    if(!lpDevice)
    {
        THROW("CreateVertexBuffer: Device is invalid.");
    }
    if(!m_pVertexBuffer)
    {
        m_lpDevice = lpDevice;
        m_uiLength = uiLength;
        m_dwUsage = dwUsage;
        m_dwFVF = dwFVF;
        m_dwPool = dwPool;

        DWORD dwRet = m_lpDevice->CreateVertexBuffer(uiLength, dwUsage, dwFVF, dwPool, &m_pVertexBuffer);
        if(dwRet != S_OK) {
            THROWERR("Cannot create Vertex buffer due", dwRet);
        }
        return 0;
    }
    return -1;
}

int XVertexBuffer::Restore()
{
    if(m_lpDevice && !m_pVertexBuffer)
    {
        DWORD dwRet = m_lpDevice->CreateVertexBuffer(m_uiLength, m_dwUsage, m_dwFVF, m_dwPool, &m_pVertexBuffer);
        if(dwRet != S_OK) {
            THROWERR("Cannot create Vertex buffer due", dwRet);
        }
        return 0;
    }
    return -1;
}

void XVertexBuffer::Release()
{
    SAFE_RELEASE(m_pVertexBuffer);
}

Ve funkci Create() je nutnΘ zßlohovat vÜechny parametry vytvß°enΘho bufferu. Tyto parametry pozd∞ji vyu₧ijeme v metod∞ Restore().

Na zßv∞r tΘto Φßsti jeÜt∞ p°idßme podporu buffer∙ do naÜeho mana₧eru zdroj∙. P°idejme dv∞ dynamickß pole:

std::vector <XVertexBuffer*> m_arVB;
std::vector <XIndexBuffer*> m_arIB;

a dvojici metod pro obsluhu t∞chto polφ:

int AddVertexBuffer(XVertexBuffer * pBufferToAdd);
int AddIndexBuffer(XIndexBuffer * pBufferToAdd);

Princip je obdobn² jako v p°φpad∞ textur a mesh∙.
 

24.2. Technika mipmap

V tΘto Φßsti si vysv∞tlφme co je to mipmapping a upravφme t°φdu XTexture tak, aby mipmapping podporovala.

24.2.1. Princip techniky

Zßkladnφm problΘmem u textur je pou₧itφ sprßvnΘ velikosti nebo detailnosti textury. Pokud pou₧ijete texturu o rozm∞rech 1024x1024, tak sice docφlφte perfektnφho vzhledu blφzk²ch objekt∙, ale vzdßlenΘ objekty budou vypadat divn∞ (je vid∞t, ₧e textura je zbyteΦn∞ detailnφ) a navφc bude vykreslovßnφ mnohem pomalejÜφ! Pokud naopak pou₧ijete texturu 16x16, tak budou vÜechny objekty vylo₧en∞ oÜklivΘ! DoporuΦenß velikost textury je 256x256. S touto texturou by m∞ly grafickΘ karty pracovat nejrychleji. N∞kdy ale pot°ebujete jen 64x64, pak vytvo°φte texturu 256x256 a v nφ polφΦka o rozm∞rech 64x64. V ka₧dΘm  polφΦku pak bude jinß textura. Sprßvnou texturu vybereme pomocφ texturov²ch sou°adnic. ProblΘm s detaily vÜak p°etrvßvß. NejlepÜφ by bylo, kdyby objekty, kterΘ jsou blφzko m∞ly detailnφ texturu a objekty vzdßlenΘ m∞ly mΘn∞ detailnφ texturu. A p°esn∞ tohle d∞lß mipmapping!

Jak to pracuje?
M∞jme zßkladnφ texturu o rozm∞rech 256x256. K tΘto zßkladnφ textu°e vytvo°φme dalÜφ textury o rozm∞rech 128x128, 64x64, 32x32, 16x16, 8x8, 4x4, 2x2, 1x1 podle ·rovn∞ mipmap. SystΘm DirectD3 pak chyt°e nanßÜφ na nejbli₧Üφ objekty nejdetailn∞jÜφ texturu, na vzdßlen∞jÜφ objekty mΘn∞ detailnφ texturu atd. Ve skuteΦnosti tak zabijete dv∞ mouchy jednou ranou: vÜechny objekty ve scΘn∞ vypadajφ tak jak majφ a zßrove≥ nepou₧φvßte zbyteΦn∞ velkΘ textury na vzdßlenΘ objekty, tak₧e vykreslovßnφ je rychlejÜφ.

Jak vytvo°φme mipmapy?
Zde je n∞kolik mo₧nostφ. Bu∩ vytvo°φte ruΦn∞ vÜechny textury jednotliv²ch ·rovnφ mipmap, kterΘ pak ruΦn∞ nahrajete. Nebo textury vytvo°φte programov∞ ze zßkladnφ textury anebo pou₧ijete funkci D3DXLoadTextrureFromFileEx(), kterß ud∞lß vÜe za vßs:) V naÜem p°φklad∞ pou₧ijeme t°etφ mo₧nost.

24.2.2. Implementace mipmap

Nynφ zavedeme podporu mipmap do naÜeho projektu. P°idßme metodu LoadTextureFromFileEx(). Metoda bude vypadat podobn∞ jako LoadTextureFromFile(), ale p°idßme navφc jeden parametr typu int. Tento parametr urΦφ poΦet ·rovnφ mimap. Je zbyteΦnΘ vytvß°et mipmapy a₧ do ·rovn∞ 1x1. Pokud v tomto parametru zadßte 1, textura se vytvo°φ bez mipmap (k tomuto ·Φelu je ale lepÜφ volat metodu LoadTextureFromFile()). V p°φpad∞, ₧e p°edßte parametr rovn² 0, vytvo°φ se kompletnφ °et∞z mipmap tj. a₧ do ·rovn∞ 1x1.. Dßle je t°eba p°idat barevn² formßt textury, proto₧e tento parametr vy₧aduje rozÜφ°enß funkce D3DXLoadTextureFromFileEx(). Formßt zφskßme ze t°φdy XDisplay. UvedenΘ dva parametry musφme ulo₧it do t°φdy XTexture, abychom mohli texturu obnovit v metod∞ Restore().

int XTexture::LoadTextureFromFileEx(LPCSTR szFileName, int iMipLevels, D3DFORMAT formatTextureFormat, LPDIRECT3DDEVICE8 lpDevice)
{
    if(!lpDevice)
    {
        THROW("LoadTextureFromFileEx: 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.");
        }
        if(D3D_OK != D3DXCreateTextureFromFileEx(lpDevice, m_szFilePath, 0, 0, iMipLevels,
                                                D3DUSAGE_RENDERTARGET, formatTextureFormat, D3DPOOL_DEFAULT,
                                                D3DX_DEFAULT , D3DX_DEFAULT, 0, NULL, NULL, &m_lpTexture))
        {
            TRACE("Texture '%s' was loaded.", szFileName);
        }
        else
        {
            TRACE("Texture '%s' wasn't loaded.", szFileName);
        }
        // save information to restore
        m_lpDevice = lpDevice;
        m_iMipLevels = iMipLevels;
        m_formatTextureFormat = formatTextureFormat;
        return 0;
    }
    return -1;
}

Upravenß metoda LoadTextureFromFileEx() je vlastn∞ stejnß jako primitivnφ verze, jen mφsto D3DXLoadTextureFromFile() pou₧φvß D3DXLoadTextureFromFileEx() a uklßdß poΦet mipmap ·rovnφ a formßt textury.

Pro ·plnost jeÜt∞ uvedu p∙vodnφ metodu LoadTextureFromFile():

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.");
        }
        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;
        m_iMipLevels = 1;
        return 0;
    }
    return -1;
}

Zde si musφme poznamenat, ₧e textura je bez mipmap, tak₧e mß pouze jednu ·rove≥.

Na zßv∞r si uvedeme rozdvojenou metodu Restore():

int XTexture::Restore()
{
    // reinit texture
    if(m_lpDevice)
    {
      
 if(m_iMipLevels == 1)
        {

            // try to create 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);
            }
       
}
        else
        {
            if(D3D_OK != D3DXCreateTextureFromFileEx(m_lpDevice, m_szFilePath, 0, 0, m_iMipLevels,
                                                    D3DUSAGE_RENDERTARGET, m_formatTextureFormat, D3DPOOL_DEFAULT,
                                                    D3DX_DEFAULT , D3DX_DEFAULT, 0, NULL, NULL, &m_lpTexture))
            {
                TRACE("Texture '%s' was reloaded.", m_szFilePath);
            }
            else
            {
                TRACE("Texture '%s' wasn't reloaded.", m_szFilePath);
            }
        }

    }
    return -1;
}

Mo₧nß jste si vÜimli t∞chto t°φ °ßdk∙ v metod∞ Init() t°φdy XDisplay:

// 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);

Takto nastavφme filtrovßnφ textur a filtrovßnφ mezi jednotliv²mi ·rovn∞mi mipmap.

Prom∞nnΘ m_tftXXXTextureFilter nastavujeme podle nßsledujφcφ tabulky v projektu Setup:

Typ filtrovßnφ Typ MAG filtru Typ MIN filtru Typ MIPMAP filtru
Bilinear filtering linear magnification linear minification point mipmapping
Trilinear filtering linear magnification linear minification linear mipmapping
Bilinear Anisotropic filtering anisotropic magnification anisotropic minification point mipmapping
Trilinear Anisotropic filtering anisotropic magnification anisotropic minification linear mipmapping

24.3. P°φklad

Na zßv∞r lekce vyzkouÜφme novΘ funkce naÜeho enginu v projektu Tester. Vytvo°φme jednoduch² Φtverec v prostoru, kter² se bude sklßdat ze Φty° vertex∙. Tyto vrcholy ulo₧φme do vertexu bufferu a indexovat je budeme pomocφ index bufferu. Dßle nahrajeme texturu, u kterΘ vytvo°φme mipmap °et∞z. Aby byl vid∞t efekt mipmap, pou₧il jsem texturu velk²ch rozm∞r∙, kterou naneseme na relativn∞ malou plochu.

Nadeklarujete tyto novΘ prom∞nnΘ:

XVertexBuffer g_vbQuad;
XIndexBuffer g_ibQuad;
XTexture g_texMipmap;
BOOL g_bQuadVis = FALSE;
BOOL g_bQuadStop = FALSE;

// world matrix
D3DXMATRIX g_matWorld1;
D3DXMATRIX g_matWorld2;

Rotujφcφ Φtverec mß jinou sv∞tovou transformaΦnφ matici (otßΦφ se kolem osy Z) ne₧ ostatnφ objekty (otßΦφ se kolem osy X), tak₧e jsem pro ob∞ transformace vytvo°il zvlßÜtnφ sv∞tovΘ matice. V p°φÜtφ lekci tuto techniku trochu vylepÜφme. Rotaci Φtverce navφc bude mo₧no zastavit mezernφkem a to urΦφ prom∞nnß g_bQuadStop. To, zda-li je Φtverec vid∞t, urΦφ prom∞nnß g_bQuadVis. Ostatnφ prom∞nnΘ jsem popsal v²Üe. Dßle p°idejme funkci, kterß naplnφ oba buffery daty:

void FillBuffers();

Tyto °ßdky p°idejte do funkce WinMain():

// init world matrices
D3DXMatrixIdentity(&g_matWorld1);
D3DXMatrixIdentity(&g_matWorld2);

// prepare quad
g_vbQuad.Create(4 * sizeof(VERTEX), D3DUSAGE_WRITEONLY, VERTEXFORMAT, D3DPOOL_DEFAULT, g_theDisplay.GetDevice());
g_ibQuad.Create(6 * sizeof(WORD), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16 , D3DPOOL_DEFAULT, g_theDisplay.GetDevice());
g_texMipmap.LoadTextureFromFileEx("grass.bmp", 5, g_theDisplay.GetTextureFormat(), g_theDisplay.GetDevice());
g_theDisplay.GetResourceManager()->AddIndexBuffer(&g_ibQuad);
g_theDisplay.GetResourceManager()->AddVertexBuffer(&g_vbQuad);
g_theDisplay.GetResourceManager()->AddTexture(&g_texMipmap);
FillBuffers();

Zde inicializujeme ob∞ matice, dßle vytvo°φme oba buffery. Mßme 4 vrcholy a 6 index∙ (dva troj·helnφky po t°ech vrcholech), vytvo°φme texturu pomocφ novΘ metody LoadTextureFromFileEx() a vÜechny zdroje vlo₧φme do mana₧eru zdroj∙. Nakonec zavolßme funkci FillBuffers(), kterß naplnφ oba buffery daty:

void FillBuffers()
{
    VERTEX *pVertices;
    g_vbQuad.GetBuffer()->Lock(0, 0, (BYTE**) &pVertices, 0);

    pVertices[0].vecPos = D3DXVECTOR3(-4.0f, -4.0f, 2.0f);
    pVertices[1].vecPos = D3DXVECTOR3(4.0f, -4.0f, 2.0f);
    pVertices[2].vecPos = D3DXVECTOR3(-4.0f, 4.0f, 2.0f);
    pVertices[3].vecPos = D3DXVECTOR3(4.0f, 4.0f, 2.0f);

    pVertices[0].vecNormal = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
    pVertices[1].vecNormal = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
    pVertices[2].vecNormal = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
    pVertices[3].vecNormal = D3DXVECTOR3(0.0f, 0.0f, 1.0f);

    pVertices[0].tu1 = 0.0f;
    pVertices[0].tv1 = 0.0f;
   
    pVertices[1].tu1 = 1.0f;
    pVertices[1].tv1 = 0.0f;

    pVertices[2].tu1 = 0.0f;
    pVertices[2].tv1 = 1.0f;
   
    pVertices[3].tu1 = 1.0f;
    pVertices[3].tv1 = 1.0f;
   
    pVertices[0].dwDiffuse = 0xFFFFFFFF;
    pVertices[1].dwDiffuse = 0xFFFFFFFF;
    pVertices[2].dwDiffuse = 0xFFFFFFFF;
    pVertices[3].dwDiffuse = 0xFFFFFFFF;

    g_vbQuad.GetBuffer()->Unlock();

   
    WORD *pIndices;
    g_ibQuad.GetBuffer()->Lock(0, 0, (BYTE**) &pIndices, 0);

    pIndices[0] = 0;
    pIndices[1] = 1;
    pIndices[2] = 2;

    pIndices[3] = 1;
    pIndices[4] = 3;
    pIndices[5] = 2;

    g_ibQuad.GetBuffer()->Unlock();
}


Tento postup jsme ji₧ pou₧ili v n∞kterΘ p°edeÜlΘ lekci. Nejprve je t°eba buffer uzamknout a tak zφskat p°φm² p°φstup do pam∞ti tohoto bufferu. Na zßv∞r je samoz°ejm∞ t°eba volat metodu Unlock() pro oba buffery.

Nynφ upravme obslu₧nou proceduru okna tak, aby se stiskem klßvesy F8 zobrazil nßÜ nov² Φtverec, ale zßrove≥ se musφ vÜechny ostatnφ objekty skr²t. Pod mezernφk navφc namapujeme funkci zastavenφ rotace Φtverce:

case VK_F9:
    g_theDisplay.RestoreDisplay();
    RestoreUserObjects();
    FillBuffers();
    break;
case VK_F1:
    g_Sphere.Visible(!g_Sphere.IsVisible());
    g_bQuadVis = FALSE;
    break;
case VK_F2:
    g_Torus.Visible(!g_Torus.IsVisible());
    g_bQuadVis = FALSE;
    break;
case VK_F3:
    g_Box.Visible(!g_Box.IsVisible());
    g_bQuadVis = FALSE;
    break;
case VK_F4:
    g_Cylinder.Visible(!g_Cylinder.IsVisible());
    g_bQuadVis = FALSE;
    break;
case VK_F5:
    g_Teapot.Visible(!g_Teapot.IsVisible());
    g_bQuadVis = FALSE;
    break;
case VK_F6:
    g_Tiger.Visible(!g_Tiger.IsVisible());
    g_bQuadVis = FALSE;
    break;
case VK_F7:
    g_Airplane.Visible(!g_Airplane.IsVisible());
    g_bQuadVis = FALSE;
    break;
case VK_F8:
    g_Torus.Visible(FALSE);
    g_Sphere.Visible(FALSE);
    g_Box.Visible(FALSE);
    g_Tiger.Visible(FALSE);
    g_Airplane.Visible(FALSE);
    g_Teapot.Visible(FALSE);
    g_Cylinder.Visible(FALSE);
    g_bQuadVis = TRUE;
    break;
case VK_SPACE:
    if(g_bQuadVis)
        g_bQuadStop = !g_bQuadStop;
    break;
case VK_ESCAPE:
    PostQuitMessage(0);
    break;

Dßle si vÜimn∞te volßnφ funkce FillBuffers() po volßnφ RestoreUserObjects(). Zde prßv∞ plnφme ji₧ novΘ buffery, kterΘ systΘm vytvo°il po resetu za°φzenφ. Nakonec zde uvedu funkci UpdateFrame():

void UpdateFrame()
{
    // svetlo se bude pohybovat po kruznici
    g_PointLight.Position.x = 0.0f;
    g_PointLight.Position.y = float(4.0f * sin(double(GetTickCount())/300.0f));
    g_PointLight.Position.z = float(5.0f * cos(double(GetTickCount())/300.0f));
    g_theDisplay.SetLight(1, &g_PointLight);
    g_theDisplay.EnableLight(1, TRUE);
    // draw scene
    g_theDisplay.UpdateBackground();
    g_theDisplay.GetDevice()->BeginScene();
    g_Sphere.Draw();
    g_Torus.Draw();
    g_Box.Draw();
    g_Cylinder.Draw();
    g_Teapot.Draw();
    // tygr nema definovane normalove vektory, takze je treba vypnout osvetleni
    g_theDisplay.GetDevice()->SetRenderState(D3DRS_LIGHTING, FALSE);
    g_Tiger.Draw();
    g_theDisplay.GetDevice()->SetRenderState(D3DRS_LIGHTING, TRUE);
    g_Airplane.Draw();

   
if(g_bQuadVis)
    {
        D3DXMATRIX matRot;
        float fFactor = cmnGetTime(TIMER_GETELAPSEDTIME1);
        if(fFactor != 0 && !g_bQuadStop) {
            D3DXMatrixRotationZ(&matRot, 0.9f * fFactor);
            D3DXMatrixMultiply(&g_matWorld2, &g_matWorld2, &matRot);
            g_theDisplay.GetDevice()->SetTransform(D3DTS_WORLD, &g_matWorld2);
        }
        g_theDisplay.GetDevice()->SetRenderState(D3DRS_LIGHTING, FALSE);
        g_theDisplay.GetDevice()->SetVertexShader(VERTEXFORMAT);
        g_theDisplay.GetDevice()->SetStreamSource(0, g_vbQuad.GetBuffer(), sizeof(VERTEX));
        g_theDisplay.GetDevice()->SetIndices(g_ibQuad.GetBuffer(), 0);
        g_theDisplay.GetDevice()->SetTexture(0, g_texMipmap.GetTexture());
        g_theDisplay.GetDevice()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 4, 0, 2);
        g_theDisplay.GetDevice()->SetRenderState(D3DRS_LIGHTING, TRUE);
    }
    else
    {
        D3DXMATRIX matRot;
        float fFactor = cmnGetTime(TIMER_GETELAPSEDTIME1);
        if(fFactor != 0) {
            D3DXMatrixRotationX(&matRot, 0.9f * fFactor);
            D3DXMatrixMultiply(&g_matWorld1, &g_matWorld1, &matRot);
            g_theDisplay.GetDevice()->SetTransform(D3DTS_WORLD, &g_matWorld1);
        }
    }

    g_theDisplay.GetDevice()->EndScene();
    if(D3DERR_DEVICENOTRESET == g_theDisplay.Present())
    {
        RestoreUserObjects();
        FillBuffers();
    }
}

Zde rozliÜujeme p°φpad, kdy je rotujφcφ Φtverec vid∞t a kdy ne. Pokud je vid∞t, je t°eba aplikovat jinou transformaci na sv∞tovou matici scΘny (matice mßme dv∞, aby pohyb obou soustav zaΦal v₧dy tam kde skonΦil) a pak vykreslφme Φtverec v t∞chto krocφch:

1) Vypnutφ sv∞tla
2) Nastav∞nφ formßtu vrchol∙
3) Nastav∞nφ zdrojovΘho vertex bufferu
4) Nastavenφ zdrojovΘho index bufferu
5) Nastavenφ textury
6) SamotnΘ vykreslenφ Φtverce
7) Zapnutφ sv∞tla

VÜechny tyto kroky jsme ji₧ rozebφrali podrobn∞ji v minul²ch lekcφch. V druhΘm p°φpad∞, pokud nenφ rotujφcφ Φtverec vid∞t, nastavφme jinou sv∞tovou matici, kterou provßdφme rotaci ostatnφch objekt∙ kolem osy X.

Nynφ se ji₧ t°φda XDisplay nestarß o transformaci sv∞tovΘ matice, tak₧e tento k≤d m∙₧ete zcela odstranit (jednß se o deklaraci matice, vytvo°enφ a samotnou transformaci matice). Dßle jsme pou₧ili strukturu VERTEX deklarovanou nßsledovn∞:

struct VERTEX
{
    D3DXVECTOR3 vecPos;
    D3DXVECTOR3 vecNormal;
    DWORD dwDiffuse;
    float tu1, tv1;
};

V naÜem p°φkladu inicializujeme i normßlovΘ vektory, ale v praxi je vlastn∞ nepou₧ijeme, proto₧e vypφnßme osv∞tlenφ.

24.4. Zßv∞r

Klßvesou F8 zobrazφte rotujφcφ Φtverec, na kterΘm je nanesena velkß textura. Kdy₧ si Φtverec zastavφte (mezernφkem) ve sprßvnΘ poloze, jsou vid∞t jednotlivß rozhranφ mipmap (nejlΘpe je to vid∞t, kdy₧ mßte nastaveno obyΦejnΘ bilineßrnφ filtrovßnφ).

V p°φÜtφ lekci se koneΦn∞ podφvßme na transformaci jednotliv²ch objekt∙ pomocφ matic.

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

Ji°φ Formßnek