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.
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φ.
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:
Uvolnit vÜechny zdroje - textury, meshe, shadery, index a vertex buffery.
Resetovat za°φzenφ a chvilku poΦkat.
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.
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∙:
inicializace systΘmu Direct3D. Nastavenφ grafiky se nahrßvß ze souboru "Setup.ini".
jednoduchß (prozatφm) sprßva zdroj∙. Zatφm jen textur a mesh∙.
korektnφ reakce na ztrßtu za°φzenφ vΦetn∞ destrukce a obnovy zdroj∙.
nahrßvanφ mesh∙ ze soubor∙ .x nebo vytvß°enφ p°eddefinovan²ch tvar∙ pomocφ funkcφ knihovny D3DX.
jednoduchΘ sledovßnφ v²konu aplikace pomocφ ukazatele FPS a omezovaΦ FPS.
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:
XDisplay - inicializace, FPS, obnova po ztrßt∞ device
XResourceManager - sprßva zdroj∙
XTexture - objekt textury
XMesh - objekt mesh - sφ¥ov² model
Struktury:
prozatφm jen struktura Resolution, kterß uchovßvß informaci o rozliÜenφ
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:
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.
Restore() - Obnova textury.
Release() - Uvoln∞nφ textury.
GetTexture() - Vracφ ukazatel na rozhranφ textury.
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;
}
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:
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.
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.
D3DXCreateBox - vytvo°φ kvßdr o zadan²ch rozm∞rech. Parametry: za°φzenφ, Üφ°ka, v²Üka, hloubka, v²sledn² mesh.
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.
D3DXCreateTeapot - vytvo°φ konviΦku:) Parametry: za°φzenφ, v²sledn² mesh. KonviΦka nemß ₧ßdnΘ dalÜφ parametry.
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.
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.
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∞:
objekty pro Direct3D (za°φzenφ a objekt D3D + nastavenφ za°φzenφ).
atributy nastavenφ formßtu, rozliÜenφ, typu za°φzenφ, pozadφ atd.
atributy pro prßci s FPS - zde pot°ebujeme vφce atribut∙, proto₧e nechceme poΦφtat FPS ka₧d² cyklus (podrobnosti se dovφte nφ₧e).
atributy pro prßci s Φasem - uchovßvßme Φas od spuÜt∞nφ aplikace a Φas od poslednφho obnovenφ snφmku.
manager zdroj∙
doΦasnΘ atributy (sv∞tlo a sv∞tovß matice). Tyto objekty jsou zde pouze do p°φÜt∞, proto₧e nßÜ "engine" jeÜt∞ neumφ transformace a sv∞tla.
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:
Init() - naΦtenφ nastavenφ ze setup.ini a inicializace za°φzenφ.
UpdateBackground() - vyΦiÜt∞nφ pozadφ a Z-bufferu (doΦasn∞ transformace sv∞tovΘ matice)
Present() - prohozenφ buffer∙, test ztrßty za°φzenφ, vykreslenφ a omezenφ FPS.
RestoreDisplay() - uvoln∞nφ vÜech zdroj∙, reset za°φzenφ a znovunaΦtenφ zdroj∙.
UpdateFPS() - v²poΦet a zobrazenφ FPS.
LimitFPS() - omezenφ FPS.
Clean() - uvoln∞nφ objekt∙ D3D.
BuildUpMatrices() - nastavenφ matic (doΦasn∞ takΘ nastavenφ sv∞tla)
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:
rozliÜenφ obrazovky
formßt (barevnß hloubka)
nastavenφ zda-li aplikace b∞₧φ v okn∞ Φi nikoliv
nastavenφ viditelnosti ukazatele FPS
po₧adovanΘ FPS - omezovaΦ. Pokud je 0, FPS je neomezenΘ.
naΦtenφ "chovßnφ" za°φzenφ - softwarovΘ Φi hardwarovΘ zpracovanφ vertex∙
formßt textur
ordinßlnφ Φφslo zvolenΘho adaptΘru
typ za°φzenφ - HAL nebo REF
filtrovßnφ textur
antialiasing
vertikßlnφ synchronizace
obnovovanφ frekvence
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.:
Uvolnφ vÜechny zdroje - textury, meshe, shadery, index a vertex buffery.
Resetuje za°φzenφ a chvilku poΦkß.
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.
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.
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.