C/C++ & Visual C++

Kurz DirectX (31.)

V minulΘ lekci jsem slφbil, ₧e se dnes podφvßme na vylepÜenφ vykreslovacφ fronty, proto₧e zp∙sob uveden² minule, byl znaΦn∞ neefektivnφ. Nejv∞tÜφ ₧rout Φasu je toti₧ funkce SetVertexStream() a tedy ΦastΘ p°epφnßnφ vertex buffer∙ vede ke zbyteΦn²m v²konov²m ztrßtßm. NaÜφm ·kolem v dneÜnφ lekci je tedy vytvo°it postup, jak minimalizovat poΦet volßnφ metody SetVertexStream().

31.1. Dynamick² index buffer

Minule jsme pou₧ili jeden index buffer, kter² byl spoleΦn² pro vÜechny listy. Mohli jsme si to dovolit, proto₧e listy se vykreslovaly jeden po druhΘm. Ka₧d² m∞l sv∙j vlastnφ vertex buffer, kter² se musel p°ed vykreslenφm KAÄD╔HO listu nastavit. Zde tedy vznikalo nadm∞rnΘ p°epφnanφ vertex buffer∙. Zato index buffer se nastavil jen jednou pomocφ metody SetIndices().

Dnes si ukß₧eme jin² zp∙sob, kter² je elegantn∞jÜφ aΦkoliv slo₧it∞jÜφ na implementaci. Budeme postupovat opaΦn∞, Φili vytvo°φme jeden velk² vertex buffer pro cel² terΘn a n∞kolik menÜφch index buffer∙, jejich₧ obsah se bude ka₧d² snφmek m∞nit. Takov² index buffer naz²vßme dynamick² a vytvß°φ se s p°φznakem D3DUSAGE_DYNAMIC. Tento p°φznak zajistφ polohu index bufferu v AGP pam∞ti (statickΘ buffery se vytvß°ejφ ve video pam∞ti). Dynamick² index buffer musφme zamykat s p°φznaky D3DLOCK_DISCARD nebo D3DLOCK_NOOVERWRITE. Prvnφ z nich zajistφ, ₧e cely bufferu bude zahozen a funkce Lock() vrßtφ ukazatel na novou oblast pam∞tφ, co₧ zamezφ Φekßnφ na vykreslenφ dat z p°edchozφho snφmku. Druh² zmφn∞n² p°φznak naopak zajistφ, ₧e se nep°epφÜφ ₧ßdnΘ indexy, kterΘ byly vlo₧en² p°edchozφm volßnφm metody Lock(). Tφmto zp∙sobem se dajφ p°idßvat indexy do bufferu.

Stejn²m zp∙sobem m∙₧eme pracovat i s vertex bufferem.

31.2. Rozd∞lenφ terΘnu

NßÜ terΘn rozd∞lφme na lokace, kterΘ budou mφt p°edem definovanou velikost (nap°φklad 128x128 polφΦek). Ka₧dß takovß lokace bude mφt vlastnφ index buffer. Lokaci definujeme strukturou Location:

struct Location
{

// Jednoznacne ID lokace (pouze kvuli vypisum)
WORD m_wID;
// Index buffer ktery ma indexy v ramci jedny lokace
IIndexBuffer *pIB;
// pocet viditelnych listu
int m_iVisQuads;
// Minimalni a maximalni pouzity index
WORD m_iMinIndex;
WORD m_iMaxIndex;
// Docasny pointer na otevreny index buffer
WORD *pIndices;
// Okrajove body
BoundingPoint arBounds[4];

};

Krom∞ zmφn∞nΘho IB struktura obsahuje n∞kolik dalÜφch parametr∙ vyu₧φvan²ch p°evß₧n∞ p°i vykreslovßnφ. Za prvΘ je to poΦet viditeln²ch list∙. Z toho parametru pozd∞ji zjistφme poΦet troj·helnφk∙, kterΘ vykreslit. Dßle jsou tu atributy MinIndex a MaxIndex, pomocφ kter²ch urΦφme rozsah pou₧it²ch vertex∙ p°i volßnφ DrawIndexedPrimitive(). Dßle je tu pomocn² ukazatel na pole index∙, kter² pou₧ijeme p°i zamykßnφ IB. Nakonec okrajovΘ body lokace, abychom mohli p°i°adit list∙m jejich lokaci.

Upravme tedy strukturu uzlu QTNode:

struct QTNode {

//
// Type of node - NODE, LEAF
NODETYPE ntType;
//
// Bounding points
BoundingPoint arBounds[4];
//
// Each node has four branches/children/kids
// LEAF has not any kids
QTNode *pBranches[4];

//
// Lokace do ktery list patri
Location *pLocation;
//
// leaf's tiles, each LEAF has 25 vertices
// (2 triangles per tile -> 32 triangles per LEAF)
// NODE has not any vertices
WORD *pIndices;
WORD wIndCount;

BOOL bVis;

};

Zde tedy p°idßme ukazatel na lokaci, kde se dan² list nachßzφ (prom∞nnou inicializujeme pouze pro listy). Dßle ji₧ nebudeme pot°ebovat vertex buffer, ale mφsto toho ulo₧φme dynamickΘ pole obsahujφcφ indexy vertex∙ listu. JeÜt∞ uklßdßme pomocnou informaci, poΦet index∙ (abychom toho Φφslo nemuseli poΦφtat pozd∞ji a uÜet°ili tak Φas).

Hlavnφ rozdφl bude v tom, ₧e cel² terΘn bude po lokacφch ulo₧en v jedinΘm Vertex bufferu. Z toho plyne, ₧e VB bude obsahovat okrajovΘ vrcholy lokacφ dvakrßt. Tφm zajistφme jednoduÜÜφ implementaci, ale zaplatφme vyÜÜφ pam∞¥ovou slo₧itostφ. U ka₧dΘ lokace budeme b∞hem rozpoznßvßnφ uklßdat viditelnΘ listy a proto₧e vφme, v jakΘ lokaci list je, zkopφrujeme danΘ indexy do IB lokace. P°i vykreslovanφ pak budeme op∞t postupovat po jednotliv²ch lokacφch. Vyu₧ijeme vÜechny parametry, kterΘ jsme si uklßdali b∞hem rozpoznßvßnφ viditeln²ch list∙. Podrobnosti si uvedeme u konkrΘtnφho algoritmu.

Upravφme takΘ samotnou t°φdu CTerrain. Ji₧ nebude pot°eba jeden IB, ale mφsto n∞j vlo₧φme globßlnφ Vertex buffer. Dßle bude pot°eba dynamickΘ pole lokacφ a poΦet lokacφ:

Location *m_arLoc;
int       m_iLocCount;

Krom∞ t∞chto atribut∙ jeÜt∞ vyma₧eme funkci FillLeaves(), proto₧e listy se budou plnit zcela jinak.

Nynφ se ji₧ m∙₧eme podφvat na zm∞ny v metodßch t°φdy CTerrain. ZaΦneme metodou InternalInit(), kde jsme minule inicializovali takΘ globßlnφ index buffer. Dnes ovÜem ₧ßdn² takov² buffer nemßme (nov² vertex buffer m∙₧eme inicializovat a₧ pozd∞ji, proto₧e jeÜt∞ nevφme rozm∞ry terΘnu). Nic novΘho v tΘto metod∞ nebude.
Metoda ComputeNormals() z∙stane beze zm∞n.
Podstatn∞ se vÜak bude liÜit metoda CreateQuadTree(). Tam toti₧ budeme vytvß°et lokace s jejich index buffery a takΘ ji₧ m∙₧eme vytvo°it vertex buffer. Zbytek metody bude stejn²:

Nejd°φve tedy urΦφme poΦet lokacφ. Vφme jak je velk² terΘn a vφme jak je velkß lokace (to je urΦeno konstantou). PotΘ alokujeme p°φsluÜenΘ mφsto a lokace jednu podruhΘ nastavφme. Zde vytvß°φme nov² objekt IIndexBuffer. Do index bufferu se musφ vejφt indexy list∙ celΘ lokace, kterß mß v naÜem p°φpad∞ 128x128 polφΦek. UrΦφme tedy poΦet list∙ v jednΘ lokaci (vφme velikost lokace a velikost listu). Ka₧dΘ polφΦko mß dva troj·helnφky, na ka₧d² jsou t°eba 3 indexy. Z toho vypoΦφtßme velikost bufferu. VÜimn∞te si, ₧e index buffer vytvß°φme s p°φznakem D3DUSAGE_DYNAMIC.
Dßle urΦφme okrajovΘ body lokace a nastavφme zbylΘ parametry na rozumnΘ hodnoty. MinIndex a MaxIndex v₧dy musφ mφt maximßlnφ a minimßlnφ hodnotu. Proto₧e index buffery jsou dynamickΘ, nenφ t°eba je plnit (plnφ se v ka₧dΘm cyklu aplikace).
V dalÜφ Φßsti vytvo°φme a naplnφme vertex buffer. Ji₧ jsem zmφnil, ₧e data ve VB jsou ulo₧eny po lokacφch. Budeme tedy op∞t prochßzet jednotlivΘ lokace a v₧dy p°idßme vertexy celΘ jednΘ lokace. Jak bude VB velk²? Znßme poΦet lokacφ a vφme kolik jedna lokace pot°ebuje vrchol∙. Z toho snadno spoΦφtßme v²slednou velikost VB. Nynφ tedy pro ka₧dou lokaci zkopφrujeme p°φsluÜnΘ vertexy z velkΘho pole m_arTerrain. Vyu₧ijeme k tomu okrajovΘ body lokacφ. VÜimn∞te si jak²m zp∙sobem zamykßme Vertex buffer, v₧dy zamkneme jen Φßst pro jednu lokaci:

Metody GenerateTerrain() a GenerateTerrainFromFile() z∙stanou nezm∞n∞ny.

DalÜφ dost podstatnΘ zm∞ny nastanou v metod∞ CullTerrain(). Prvnφ Φßst z∙stane stejnß, liÜit se bude pouze Φßst, kde p°idßvßme nov² list do fronty:

Za prvΘ spoΦφtßme posun v index bufferu danΘ lokace (zde ji₧ majφ listy odkaz na svou lokaci - viz. dßle). Vφme kolik list∙ je ji₧ v tomto IB a takΘ vφme kolik index∙ jeden list zabere. P°ekopφrujeme indexy funkcφ memcpy(). IB ji₧ mßme otev°en² (viz. metoda Render()) a ukazatel je ulo₧en v atributu pIndices. Dßle urΦφme maximßlnφ a minimßlnφ pou₧it² index. Minimßlnφ index mß vrchol nejblφ₧e k poΦßtku, maximßlnφ je naopak ten nejdßl v rßmci jednoho listu. Tyto hodnoty platφ pro celou lokaci, tudφ₧ je musφme testovat pro ka₧d² list, kter² vklßdßme do IB. Nakonec musφme zv²Üit hodnotu viditeln²ch list∙ (aby se dalÜφ nalezen² list vlo₧il do IB nakonec). Zßznam, ₧e list je vid∞t je d∙le₧it² jen kv∙li map∞, kterou lze zobrazit klßvesou M.

Metoda Render() zaznamenala taky spoustu zm∞n. V prvnφ Φßsti p°ipravφme Index buffery. Tato Φßst lze zcela p°eskoΦit (pak je hezky vid∞t vykreslovanß oblast). Dßle provedeme samotnΘ vykreslenφ:

Op∞t zde uvßdφm jen ΦßsteΦn² v²pis, zbytek metody je stejn². Jak jsem tedy zmφnil, nejd°φve uzamkneme IB vÜech lokacφ a vynulujeme parametry. VÜimn∞te si jak uklßdßme ukazatele na otev°enΘ IB. Tyto ukazatele vzßp∞tφ pou₧ijeme v metod∞ CullTerrain(). Nakonec je t°eba IB op∞t odemknout. P°ed vykreslenφm nastavφme standardnφ v∞ci jako te textura, vertex shader a nßÜ nov² VB. Metoda SetStreamSource() se tedy volß jen jednou za cyklus. Nynφ se podrobn∞ podφvßme na zp∙sob vykreslovßnφ.
To, ₧e terΘn vykreslujeme po lokacφch je asi celkem jasnΘ. Mß smysl kreslit jen ty, kterΘ majφ n∞jak² viditeln² list. Te∩ p°ijde nejv∞tÜφ finta v nastavenφ bßzovΘ hodnoty indexu v metod∞ SetIndices() (je to druh² parametr). Tato hodnota se b∞hem vykreslovanφ p°iΦφtß ke vÜem index∙m ulo₧en²ch v IB. Tφm dosßhneme na vÜechny vertexy. P°i volßnφ metody DrawIndexedPrimitive() vyu₧ijeme atributy lokace MinIndex, MaxIndex a iVisLeaves.
Zbytek metody je stejn².

Metoda Restore() nynφ nebude obnovovat Index buffer pro list, ale vertex buffer pro terΘn. V²pis metody zde nebudu uvßd∞t, proto₧e se p°φliÜ neliÜφ od inicializace bufferu.

Jako poslednφ lah∙dku zde mßme metodu CreateNode(). I tato metoda z∙stane vesm∞s stejnß, jen prßce s listy bude odliÜnß:

Metoda mß za ·kol vytvo°it a naplnit pole index∙ ka₧dΘho listu a p°i°adit list jednΘ lokaci. Abychom zjistili, ve kterΘ lokaci se dan² list nachßzφ, pou₧ijeme okrajovΘ body lokacφ a listu. Kdy₧ najdeme tu sprßvnou lokaci, hledßnφ ukonΦφme.
Dßle alokujeme mφsto pro indexy. PoΦet index∙ listu snadno urΦφme z toho, ₧e vφme jak je list velik². Zb²vß pole naplnit daty. To je pom∞rn∞ obtφ₧nß operace. Indexy toti₧ musφ mφt hodnoty v rßmci jednΘ lokace. My zde vyu₧ijeme okrajovΘ body listu, to by ale nestaΦilo, proto₧e nap°φklad list z druhΘ lokace by m∞l indexy mimo rozsah. Tak₧e aby indexy byly ve sprßvnΘm rozsahu, pou₧ijeme d∞lenφ modulo.

Poznßmka: Jist∞ jste si vÜimli, ₧e se v k≤du vyskytujφ °ßdky, kterΘ zajistφ v²pis obsahuj Vertex buffer a index buffer do souboru. Tato volba je volitelnß a m∙₧e se hodit p°i lad∞nφ programu. V²pis t∞chto dat do v²stupnφho okna v²vojovΘho prost°edφ nenφ vhodn², proto₧e je p°φliÜ pomal².

30.3. Zßv∞r

A co nßs Φekß v p°φÜtφ lekci? Dod∞lßme slφbenou oblohu a pohrajeme se s funkcemi mlhy. Direct3D mß docela sluÜnou podporu mlhy, tak₧e to nebude nic obtφ₧nΘho.

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

 

Ond°ej BuriÜin a Ji°φ Formßnek