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