4. diel - XNA tvorba v 3D - Modely
Vitajte piatykrát, minule sme prebrali súradnice a matice 3D sveta. V tomto diele sa pozrieme na vykresľovanie modelu. Áno, už konečne model. Vedú ma k tomu neodkladnej skutočnosti posunúť sa od jednoduchých objektov, na ktorých sa síce všetko dobre vysvetľuje a názorne ukazuje, ale model je predsa model.
Model je v XNA
reprezentovaný triedou Model
. Ako
by sa dalo čakať. Model nie je nič iné, než sústava trojuholníkov. Sú to
tie jedny a isté trojuholníky, s ktorými sme si v predchádzajúcich dieloch
hrali. XNA
dovedie nahrať modely vo formáte .x
, čo
je akýsi DirectX formát, ale ja skôr odporúčam formát fbx
. Do
neho dovedú exportovať všetky bežne používané modelovacie programy. (iz
sketch-upu sa nám to podarilo!) Len pozor, musí byť export do FBX
verzie 2010. Odporúčam tiež ukladať v
ASCII režimu a nie v binárnom, ako sa napokon presvedčíte
nižšie. Často je potrebné do modelu siahnuť a upraviť adresy pre textúry
na relatívna. Zaujímavý, no anglický článok o možných úskaliach exporte
z obľúbeného 3dsmax nájdete tu, ďakujem zmijozelákovi
za poskytnutie. Pre naše skúšanie som náhodne vybral jeden z modelov z
projektu ŽvB2. Je to perzský koberček. Nájdete ho dole v archíve. Do
projektu ho dostaneme rovnako ako textúru. Projekt som opäť vyčistil, aby sa
nám tam naše predošlé výtvory neplietli. Vytvoríme si premennú pre
model:
Model model;
V metóde LoadContent
model nahráme:
model = Content.Load<Model>("koberec1");
Teraz skúsime projekt skompilovať. Všetko bez problémov. Že nie? No isteže nie, inak by som to predsa po vás nechcel. Dostali sme túto chybu:
Missing asset "C:\Documents and Settings\Prokop\Dokumenty\PVR\Textury\koberec1_uvw mapa_3.png"
Nemôže sa nájsť textúra nášho koberca. Áno, to je pochopiteľné, nie
na všetkých počítačoch sa volá účet Prokop a nie na všetkých
počítačoch budeme mať textúru v tejto zložke. Tu práve prichádza na rad
to, prečo je dobré mať model v ASCII
formáte. Ak by súbor bol
uložený binárne, tak sa samozrejme nič nedeje. Možno ho prekonvertovať
treba týmto
nástrojom priamo od Autodesku. Opäť pozor na verziu, tá musí byť opäť
2010. Možno ho ľahko otvoriť v ľubovoľnom textovom editore
(myslený nie Word) a mierne upraviť k obrazu nášmu. V súbore potom stačí
nájsť miesta, kde sa všade nachádza Prokop
alebo prípadne
PRV
alebo iná význačná časť z existujúcej adresy súboru.
Väčšinu adresy zašpiníme a necháme iba:
koberec1_uvw mapa_3.png
Zmenu je u tohto súboru nutné vykonať celkom na štyroch riadkoch
(1848,1849,1874,1875). Túto činnosť možno určite automatizovať potrebné
nejakým plug-inom do Visual Studia. Ak by mal niekto záujem pomôcť a treba u
toho rovno napísať, ako sa mu podarilo ten plug-in urobiť, má tu
príležitosť. Ďalej musíme súbor s textúrou nahrať do zložky Content.
Textúru tiež nájdete v archíve, ale tentokrát už na svojom mieste v
priečinku Content. Nemusíme ju vkladať cez Visual Studio, pretože je na neho
odkaz má náš model a XNA
si ho vyzdvihne samo. Ak teraz projekt
skompilujeme, všetko sa už podarí bez chýb a my môžeme pokračovať
ďalej. Vytvoríme si pole typu Matrix
, kde budeme skladovať
transformácie danej priamo modelovacím programom.
Matrix[] transforms;
Áno počujete dobre, transformácia, nie je to nič iné ako matice
World
, ktorá je však v tomto prípade daná priamo z
modelovacieho programu. Ide opäť o natočenie modeli, zmenu mierky a podobne,
ale tentoraz nie od nás, ale od modelovacieho programu. Presunieme sa do
metódy LoadContent
, kde práve vytvorené pole inicializujeme a
naplníme hodnotami:
transforms=new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(transforms);
Ďalej si pripravíme objekt pre samotné vykreslenie. Rovnako ako u
trojuholníka aj tu musíme nastaviť parametre efektu (shader, zvykajte si aj
na tento pojem) a to konkrétne všetky tri matice. Lenže model nie je tak
jednoduchá záležitosť ako jeden trojuholník. Iste, skladá sa z nich tiež,
ale jeho štruktúra je zložitejšia. XNA
rozlišuje takzvané
meshe, reprezentované triedou ModelMesh
. Pod týmto pojmom si
možno predstaviť jeden súvislý povrch modelu. Trebárs hlavu postavy. Ďalej
každý / á mesh má svoje časti. Tu nazvanej meshpart, a to v triede
ModelMeshPart
. Poväčšine má každá mesh iba jednu časť.
Záleží ale na jej komplikovanosti. Práve tieto časti v sebe obsahujú už
pripravené efekty, ktoré čakajú na nastavenie hodnôt. Nastavíme teda
všade hodnoty matíc View
, Projection
a
World
a povolíme akési základné osvetlenie:
foreach (ModelMesh mesh in model.Meshes){ foreach (ModelMeshPart part in mesh.MeshParts){ BasicEffect ef = part.Effect as BasicEffect; ef.View = Matrix.CreateLookAt(new Vector3(100, 150, 0), new Vector3(0, 0, 0), Vector3.Up); ef.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver2, GraphicsDevice.DisplayMode.AspectRatio, 1, 1000); ef.World = transforms[mesh.ParentBone.Index]; ef.EnableDefaultLighting(); } }
Iste, nie je to optimálne riešenie. Matica je možné vypočítať aj mimo
cyklus a potom len nastavovať. Všimnite si nastavenie matice
World
. Tu používame hodnoty z pripraveného poľa. Teraz sa
premiestnime do metódy Draw
. Ako vykresliť model? Máme hneď
niekoľko možností. Je ale dobré sa už teraz naučiť túto, pretože sa
nám bude v neskorších dielach viac hodiť:
foreach (ModelMesh mesh in model.Meshes){ mesh.Draw(); }
To je všetko. Nič moc ťažkého alebo náročného. Ak teraz program spustíte, mali by ste vidieť nádherný perzský koberec od JUnit (týmto ďakujem, že mi ten model požičal, pamätaj na trojku !!).
Skvele. A keďže sa toho zdá byť málo tak pridáme jednoduchú animáciu. Vytvoríme si premennú s maticou pre rotáciu:
Matrix rotace;
V metóde Initialize
ju nastavíme na východiskovú
hodnotu:
rotace = Matrix.Identity;
Matica Identity
(slovensky jednotková) je taká matica, ktorá
má na svoje hlavné diagonále samé jednotky. Je to jediná matice, s ktorou
keď vynásobíme maticu inú, tak výsledok je rovnaký ako predtým. V metóde
Update
pridáme zväčšenie rotácie o nejaký malý uhol.
Otáčame okolo osi Y:
rotace *= Matrix.CreateRotationY(0.01f);
Násobíme predošlú maticu maticou novú, tým dosiahneme to, že sa uhly
spočítajú. Potom už len v metóde Draw
nastavíme nový
parameter do World
matice a môžeme model opäť vykresliť:
foreach (ModelMesh mesh in model.Meshes){ foreach (ModelMeshPart part in mesh.MeshParts){ BasicEffect ef = part.Effect as BasicEffect; ef.World = transforms[mesh.ParentBone.Index]*rotace; } mesh.Draw(); }
Všimnite si, že násobíme maticu World
z editora a maticou
našej. Nie naopak. Inak by výsledok nebol ten, ktorý požadujeme. Schválne
si to skúste. Násobenie matíc nie je komutatívna teda A * B nie je to isté
ako B * A. Uvedený spôsob animácií nie je však optimálna. Veľa záleží
na FPS, ako sa z tohto problému vymaniť si ukážeme zase niekedy
nabudúce.
Tak a to je pre dnešok všetko. Opäť sa teším na reakcie a komentáre, ktoré zatiaľ prichádzajú len keď si o ne poviem. Ako úlohu si skúste s pomocou návodov z 2D série napísať jednoduché ovládanie otáčania modelu pomocou treba šípok. Nabudúce sa pozrieme na ... a viete čo nechajte sa prekvapiť.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 394x (1.58 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#