DirectX (25.)

V dneÜnφ lekci se koneΦn∞ dostaneme k dlouho slibovan²m transformacφm objekt∙. Dßle rozÜφ°φme projekt Display o t°φdu X3DObject, kterß zapouzd°φ vÜe, co jsme minule pou₧ili pro nßÜ rotujφcφ trßvnφk.

25.1. Transformace objekt∙

Na zaΦßtku jsme do naÜeho projektu p°idali t°φdu CMesh, kterß zapouzd°uje objekt naΦten² ze souboru. Nev²hodou bylo, ₧e jsme nemohli transformovat ka₧d² vytvo°en² objekt samostatn∞, zkrßtka jsme m∞li pouze jednu transformaΦnφ matici pro vÜechny objekty ve scΘn∞. Dnes do tΘto t°φdy p°idßme prßv∞ transformaΦnφ matici, tak₧e ka₧d² objekt bude transformovßn zvlßÜ¥. Tuto matici nastavφme p°ed ka₧d²m vykreslenφm toho danΘho objektu jako sv∞tovou. VÜechny vertexy objektu se tedy budou transformovat podle tΘto matice. Dßle p°idßme sadu Φlensk²ch funkcφ, pomocφ kter²ch budeme tuto matici upravovat - transformovat a tφm vytvß°et a sklßdat zßkladnφ transformace na objekt. O transformacφch jsme mluvili v jednΘ z prvnφch lekcφch. Mßme tedy t°i zßkladnφ transformace, pomocφ kter²ch vytvo°φme libovoln² pohyb objektu. Jsou to:

 1) pohyb posuvn² - translace
 2) pohyb otßΦiv² - rotace
 3) zm∞na m∞°φtka

Ke vÜem t∞mto transformacφm existujφ funkce z knihovny D3DX, kterΘ vytvß°ejφ po₧adovanΘ matice. V²hodou matic je, ₧e jdou sklßdat a m∙₧eme tak slo₧it vφce efekt∙ dohromady. I k tomu nßm pom∙₧e pßr funkcφ z knihovny D3DX.

P°ejd∞me tedy rovnou k p°φkladu.

Do t°φdy XMesh p°idßme atribut D3DXMATRIX:

D3DXMATRIX *m_lpTransMat;

Dßle vytvo°φme sadu Φlensk²ch funkcφ pro transformaci objektu:

void Translate(float fX, float fY, float fZ);
void RotateX(float fAngle);
void RotateY(float fAngle);
void RotateZ(float fAngle);
void ScaleXYZ(float fFactor);
void ScaleX(float fFactor);
void ScaleY(float fFactor);
void ScaleZ(float fFactor);

Objekt m_lpTransMat musφme v konstruktoru vytvo°it a v destruktoru zniΦit:

XMesh::XMesh(void)
{
    m_lpMesh = NULL;
    m_lpDevice = NULL;
    m_bVisible = FALSE;
    m_lpTransMat = new D3DXMATRIX;
    D3DXMatrixIdentity(m_lpTransMat);
}

XMesh::~XMesh(void)
{
    Release();
    m_lpDevice = NULL;
    SAFE_DELETE_ARRAY(m_pMaterials);
    SAFE_DELETE_ARRAY(m_pTextures);
    SAFE_DELETE(m_lpTransMat);
}

Nakonec nesmφme zapomenout nastavit matici t∞sn∞ p°ed vykreslenφm:

int XMesh::Draw()
{
    if(m_lpDevice && m_bVisible && m_lpMesh)
    {
        // Set transformation matrix before rendering
        if(m_lpTransMat)
        {
            m_lpDevice->SetTransform(D3DTS_WORLD, m_lpTransMat);
        }
        if(m_meshType == CUSTOM)
        {
            for(int i = 0; i < (int)m_dwNumMat; i++)
            {
                m_lpDevice->SetMaterial(&m_pMaterials[i]);
                m_lpDevice->SetTexture(0, m_pTextures[i].GetTexture());
                m_lpMesh->DrawSubset(i);
            }
        }
        else
        {
            // set default material
            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_lpDevice->SetMaterial(&mtrl);

            m_lpDevice->SetTexture(0, NULL);
            m_lpMesh->DrawSubset(0);
        }
        return 0;
    }
    return -1;
}

A implementujeme transformaΦnφ metody, kterΘ jen modifikujφ transformaΦnφ matici:

void XMesh::Translate(float fX, float fY, float fZ)
{
    D3DXMATRIX matTemp;
    // Create translation matrix
    D3DXMatrixTranslation(&matTemp, fX, fY, fZ);
    // modify transformation matrix of the object
    D3DXMatrixMultiply(m_lpTransMat, m_lpTransMat, &matTemp);
}

void XMesh::RotateX(float fAngle)
{
    D3DXMATRIX matTemp;
    // create rotation matrix
    D3DXMatrixRotationX(&matTemp, fAngle);
    // modify transformation matrix of the object
    D3DXMatrixMultiply(m_lpTransMat, m_lpTransMat, &matTemp);
}

void XMesh::RotateY(float fAngle)
{
    D3DXMATRIX matTemp;
    // create rotation matrix
    D3DXMatrixRotationY(&matTemp, fAngle);
    // modify transformation matrix of the object
    D3DXMatrixMultiply(m_lpTransMat, m_lpTransMat, &matTemp);
}

void XMesh::RotateZ(float fAngle)
{
    D3DXMATRIX matTemp;
    // create rotation matrix
    D3DXMatrixRotationZ(&matTemp, fAngle);
    // modify transformation matrix of the object
    D3DXMatrixMultiply(m_lpTransMat, m_lpTransMat, &matTemp);
}

void XMesh::ScaleXYZ(float fFactor)
{
    D3DXMATRIX matTemp;
    // Create scaling matrix
    D3DXMatrixScaling(&matTemp, fFactor, fFactor, fFactor);
    // modify transformation matrix of the object
    D3DXMatrixMultiply(m_lpTransMat, m_lpTransMat, &matTemp);
}

void XMesh::ScaleX(float fFactor)
{
    D3DXMATRIX matTemp;
    // Create scaling matrix
    D3DXMatrixScaling(&matTemp, fFactor, 1.0f, 1.0f);
    // modify transformation matrix of the object
    D3DXMatrixMultiply(m_lpTransMat, m_lpTransMat, &matTemp);
}

void XMesh::ScaleY(float fFactor)
{
    D3DXMATRIX matTemp;
    // Create scaling matrix
    D3DXMatrixScaling(&matTemp, 1.0f, fFactor, 1.0f);
    // modify transformation matrix of the object
    D3DXMatrixMultiply(m_lpTransMat, m_lpTransMat, &matTemp);
}

void XMesh::ScaleZ(float fFactor)
{
    D3DXMATRIX matTemp;
    // Create scaling matrix
    D3DXMatrixScaling(&matTemp, 1.0f, 1.0f, fFactor);
    // modify transformation matrix of the object
    D3DXMatrixMultiply(m_lpTransMat, m_lpTransMat, &matTemp);
}

Na zaΦßtku je matice jednotkovß, to znamenß, ₧e neprovßdφ ₧ßdnou transformaci. JeÜt∞ trochu upravφme projekt Tester, abychom vyzkouÜeli vÜechny novΘ metody. Nynφ u₧ nebudeme pot°ebovat transformaΦnφ matici g_matWorld1. Tuto matici zastupujφ postupn∞ vÜechny matice jednotliv²ch objekt∙. Tak₧e mφsto ·pravy tΘto matice v funkci UpdateFrame() budeme upravovat matice objekt∙ podle pot°eby:

D3DXMATRIX matRot;
float fFactor = cmnGetTime(TIMER_GETELAPSEDTIME1);
if(fFactor != 0) {
    g_Torus.RotateY(0.8f * fFactor);
    g_Cylinder.RotateX(1.0f * fFactor);
    g_Box.RotateZ(3.0f * fFactor);
    g_Teapot.RotateZ(-0.1f * fFactor);
    g_Airplane.RotateZ(1.8f * fFactor);
    g_Tiger.RotateZ(5.8f * fFactor);
    g_Sphere.RotateY(6.0f * fFactor);
}

Navφc m∙₧eme ka₧d² objekt uvΘst do poΦßteΦnφho stavu v inicializaΦnφ funkci:

// preddefinovane objekty
g_Sphere.CreateSphere(2.0f, 48, 48, g_theDisplay.GetDevice());
g_Sphere.Translate(4.0f, 0.0f, -30.0f);
g_Sphere.ScaleXYZ(0.1f);
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(0.5f, 2.0f, 64, 64, g_theDisplay.GetDevice());
g_Teapot.CreateTeapot(g_theDisplay.GetDevice());
g_Teapot.RotateX(D3DX_PI/2);
g_Teapot.ScaleXYZ(0.5f);
g_Teapot.Translate(4.5f, 0.0f, 0.0f);
// model tygra
g_Tiger.LoadMeshFromFile("tiger.x", g_theDisplay.GetDevice(), g_theDisplay.GetResourceManager());
g_Tiger.RotateX(D3DX_PI/2);
g_Tiger.ScaleY(0.5f);
g_Tiger.Translate(0.0f, 0.0f, 4.0f);
g_Airplane.LoadMeshFromFile("airplane 2.x", g_theDisplay.GetDevice(), g_theDisplay.GetResourceManager());
g_Airplane.ScaleXYZ(0.1f);
g_Airplane.Translate(6.0f, 0.0f, 0.0f);
g_Airplane.RotateX(D3DX_PI/2);
 

25.2. T°φda X3DObject

Kdy₧ jsme minule vytvß°eli rotujφcφ Φtverec, mohli jsme vÜechny jeho atributy zapouzd°it do n∞jakΘ t°φdy. V tΘto lekci tuto t°φdu vytvo°φme a nazveme jφ X3DObject. Bude obsahovat vÜechny atributy jako nßÜ Φtverec a krom toho jeÜt∞ n∞kolik dopl≥ujφcφch polo₧ek jako je transformaΦnφ matice atd. Jak se tento objekt liÜφ od XMesh? XMesh nahrßvßme v∞tÜinou ze souboru .x. Naopak ve t°φd∞ X3DObject budeme pracovat s vlastnφmi vertex a index buffery, tak₧e tak m∙₧eme vytvo°it nap°φklad povrch zem∞ apod. Tato t°φda ale bude mφt velice podobnΘ metody, jen bude vykreslovat data z jinΘho zdroje. Deklaraci t°φdy zapφÜeme takto:

class DISPLAY_API X3DObject
{
    // is object visible?
    BOOL m_bVisible;
    // set trans matrix before render?
    BOOL m_bApplyTransformations;
    // vertex buffer
    XVertexBuffer m_VB;
    // index buffer
    XIndexBuffer m_IB;
    // texture
    XTexture m_Texture;
    // transformation matrix
    D3DXMATRIX *m_lpTransMat;
    // pointer to device
    LPDIRECT3DDEVICE8 m_lpDevice;

public:
    // standard methods
    int Init( LPDIRECT3DDEVICE8 lpDevice,
              LPCSTR szTexture, int iMipMap, D3DFORMAT formatTexture ,
              DWORD dwVBSize,
              DWORD dwIBSize,
              DWORD dwFlags);
    int Draw( D3DPRIMITIVETYPE Type,
              UINT MinIndex,
              UINT NumVertices,
              UINT StartIndex,
              UINT PrimitiveCount);
    int Restore();
    void Release();
    XVertexBuffer* GetVB() { return &m_VB; }
    XIndexBuffer* GetIB() { return &m_IB; }
    XTexture* GetTexture() { return &m_Texture; }
    LPDIRECT3DDEVICE8 GetDevice() { return m_lpDevice; }
    BOOL IsVisible() { return m_bVisible; }
    void Visible(BOOL bVis = TRUE) { m_bVisible = bVis; }
    void EnableTransformation(BOOL bEnable = TRUE) {m_bApplyTransformations = bEnable;}
    BOOL IsTransformationEnabled() { return m_bApplyTransformations;}

    // transformations methods
    void Translate(float fX, float fY, float fZ);
    void RotateX(float fAngle);
    void RotateY(float fAngle);
    void RotateZ(float fAngle);
    void ScaleXYZ(float fFactor);
    void ScaleX(float fFactor);
    void ScaleY(float fFactor);
    void ScaleZ(float fFactor);

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

 

Na prvnφ pohled se zdß b²t t°φda slo₧itß, ale je to velice jednoduchΘ. Ve skuteΦnosti jen zjednoduÜujeme implementaci Φtverce z minulΘ lekce. VÜimn∞te si stejn²ch atribut∙. Navφc jsme p°idali transformaΦnφ matici a "vypφnaΦ" vlastnφ transformace, abychom mohli rotaci vypnout. Samoz°ejm∞ si objekt pamatuje takΘ ukazatel za°φzenφ.

Dßle nadefinujeme p°φznaky metody Init(), kterΘ nastavφme, pokud chceme vytvo°it VB a IB s vlastnostφ WRITE ONLY, kterß urΦφ, ₧e danΘ buffery jsou urΦeny pouze pro zßpis a jsou umφst∞ny v h∙°e p°φstupnΘ pam∞ti pro u₧ivatele, ale lΘpe p°φstupnΘ pro hardware:

#define VB_WRITEONLY 0x00000001
#define IB_WRITEONLY 0x00000002

A nynφ u₧ jen nadefinujeme jednotlivΘ metody. Zde nenφ nic nepochopitelnΘho. Transformace je naprosto toto₧nß jako v p°φpad∞ t°φdy XMesh, jen se provßdφ pokud je zapnuta atributem m_bApplyTransformations, tak₧e tyto metody zcela vynechßm. Budeme tedy implementovat jen metody Init(), Release(), Draw(), Restore(), konstruktor a destruktor.
 

ZaΦneme konstruktorem a destruktorem:

X3DObject::X3DObject(void)
{
    m_lpDevice = NULL;
    m_lpTransMat = new D3DXMATRIX;
    D3DXMatrixIdentity(m_lpTransMat);
}

X3DObject::~X3DObject(void)
{
    SAFE_DELETE(m_lpTransMat);
}

Zde vytvo°φme objekt transformaΦnφ matice a vynulujeme pointer na za°φzenφ, podle kterΘho poznßme, ₧e objekt jeÜt∞ nebyl inicializovßn.

Metoda Init():

int X3DObject::Init(LPDIRECT3DDEVICE8 lpDevice,
                    LPCSTR szTexture, int iMipMap, D3DFORMAT formatTexture,
                    DWORD dwVBSize,
                    DWORD dwIBSize,
                    DWORD dwFlags)
{
    DWORD dwUsage = 0;
    int iRet;
    m_lpDevice = lpDevice;
    if(m_lpDevice)
    {
        if(dwVBSize != 0)
        {
            if(VB_WRITEONLY & dwFlags)
            {
                dwUsage |= D3DUSAGE_WRITEONLY;
            }
            iRet = m_VB.Create(dwVBSize, dwUsage, VERTEXFORMAT, D3DPOOL_DEFAULT, m_lpDevice);
            if(iRet != 0)
            {
                TRACE("Cannot create VB for 3DObject.");
            }
        }
        if(dwIBSize != 0)
        {
            dwUsage = 0;
            if(IB_WRITEONLY & dwFlags)
            {
                dwUsage |= D3DUSAGE_WRITEONLY;
            }
            iRet = m_IB.Create(dwIBSize, dwUsage, D3DFMT_INDEX16, D3DPOOL_DEFAULT, m_lpDevice);
            if(iRet != 0)
            {
                TRACE("Cannot create IB for 3DObject.");
            }
        }
        if(szTexture)
        {
            iRet = m_Texture.LoadTextureFromFileEx(szTexture, iMipMap, formatTexture, lpDevice);
            if(iRet != 0)
            {
                TRACE("Cannot create texure.");
            }
        }
        m_bVisible = TRUE;
        m_bApplyTransformations = TRUE;
        return S_OK;
    }
    return S_FALSE;
}

Zde musφme vytvo°it vÜechny dφlΦφ objekty: VB, IB a texturu. Jako parametry posφlßme ukazatel na za°φzenφ, jmΘno textury, poΦet mipmap a formßt textury, dßle velikost VB a IB a nakonec parametr p°φznak∙ pro dalÜφ vlastnosti, zatφm tedy jen pro parametry WRITE ONLY.

Metoda pro vykreslenφ Draw():

int X3DObject::Draw(D3DPRIMITIVETYPE Type,
                    UINT MinIndex,
                    UINT NumVertices,
                    UINT StartIndex,
                    UINT PrimitiveCount)
{
    if(m_lpDevice)
    {
        if(m_lpTransMat)
        {
            m_lpDevice->SetTransform(D3DTS_WORLD, m_lpTransMat);
        }
        if(m_bVisible)
        {
            m_lpDevice->SetVertexShader(VERTEXFORMAT);
            m_lpDevice->SetStreamSource(0, m_VB.GetBuffer(), sizeof(VERTEX));
            m_lpDevice->SetIndices(m_IB.GetBuffer(), 0);
            m_lpDevice->SetTexture(0, m_Texture.GetTexture());
            m_lpDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 4, 0, 2);
        }
        // set indentity matrix
        if(m_lpTransMat)
        {
            D3DXMATRIX mat;
            D3DXMatrixIdentity(&mat);
            m_lpDevice->SetTransform(D3DTS_WORLD, &mat);
        }
        return S_OK;
    }
    return S_FALSE;
}

Zde nastavφme transformaci a pokud je objekt viditeln² vykreslφme ho ·pln∞ stejn∞ jako v minulΘ lekci. VÜechny parametry metody DrawIndexedPrimitive() jsou zßrove≥ parametry samotnΘ metody Draw(). Na konci je t°eba nastavit jednotkovou matici pro objekty, kterΘ nenastavujφ vlastnφ matici.

Nakonec dod∞lßme metody Release() a Restore(), kde jen volßme ΦlenskΘ funkce pod-objekt∙ VB, IB a textury:

int X3DObject::Restore()
{
    if(m_lpDevice)
    {
        m_Texture.Restore();
        m_VB.Restore();
        m_IB.Restore();
        // after that, caller must refill buffers
        return S_OK;
    }
    return S_FALSE;
    }

void X3DObject::Release()
{
    if(m_lpDevice)
    {
        m_Texture.Release();
        m_VB.Release();
        m_IB.Release();
    }
}

Ve vÜech metodßch kontrolujeme ukazatel na za°φzenφ, tak poznßme, ₧e byla volßna metoda Init().

Nynφ m∙₧eme rotujφcφ Φtverec vytvo°it pomocφ t°φdy X3DObject. Zdrojov² k≤d najdete v p°φkladu v sekci Downloads.

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

Ji°φ Formßnek