Lekce 4 - XNA tvorba ve 3D - Modely
Vítejte popáté, minule, XNA tvorba ve 3D - souřadnice a matice 3D světa, jsme probrali souřadnice a matice 3D světa.
V tomto díle se podíváme na vykreslování modelu. Ano, již konečně model. Vedou mě k tomu neodkladné skutečnosti posunout se od snadných objektů, na kterých se sice vše dobře vysvětluje a názorně ukazuje, ale model je přece model.
Model je v XNA
reprezentován třídou Model
. Jak
by se dalo čekat. Model není nic jiného, než soustava trojúhelníků. Jsou
to ty jedny a samé trojúhelníky, se kterými jsme si v předchozích dílech
hráli. XNA
dovede nahrát modely ve formátu .x
, což
je jakýsi DirectX formát, ale já spíše doporučuji formát
fbx
. Do něj dovedou exportovat všechny běžně používané
modelovací programy. (i z sketch-upu se nám to povedlo!) Jenom pozor, musí
být export do fbx verze 2010. Doporučuji také ukládat v
ASCII režimu a ne v binárním, jak se ostatně
přesvědčíte nížeji. Často je potřeba do modelu šáhnout a upravit
adresy pro textury na relativní. Zajímavý, leč anglický článek o
možných úskalích exportu z oblíbeného 3dsmaxu naleznete zde, děkuji zmijozelákovi
za poskytnutí. Pro naše zkoušení jsem namátkově vybral jeden z modelů z
projektu ŽvB2. Je to perský kobereček. Naleznete jej dole v archivu. Do
projektu jej dostaneme stejně jako texturu. Projekt jsem opět vyčistil, aby
se nám tam naše předešlé výtvory nepletly. Vytvoříme si proměnnou pro
model:
Model model;
V metodě LoadContent
model nahrajeme:
model = Content.Load<Model>("koberec1");
Nyní zkusíme projekt zkompilovat. Vše bez problémů. Že ne? No jistěže ne, jinak bych to přeci po vás nechtěl. Dostali jsme tuto chybu:
Missing asset "C:\Documents and Settings\Prokop\Dokumenty\PVR\Textury\koberec1_uvw mapa_3.png"
Nemůže se najít textura našeho koberce. Ano, to je pochopitelné, ne na
všech počítačích se jmenuje účet Prokop a ne na všech počítačích
budeme mít texturu v této složce. Zde právě přichází na řadu to, proč
je dobré mít model v ASCII
formátu. Pokud by soubor byl uložen
binárně, tak se samozřejmě nic neděje. Lze jej překonvertovat třeba tímto
nástrojem přímo od Autodesku. Opět pozor na verzi, ta musí být opět
2010. Lze jej snadno otevřít v libovolném textovém editoru
(myšlen ne Word) a mírně upravit k obrazu našemu. V souboru pak stačí
najít místa, kde se všude nachází Prokop
nebo případně
PRV
nebo jiná význačná část ze stávající adresy souboru.
Většinu adresy umažeme a necháme pouze:
koberec1_uvw mapa_3.png
Změnu je u tohoto souboru nutné provést celkem na čtyřech řádcích
(1848,1849,1874,1875). Tuto činnost lze určitě automatizovat třeba
nějakým plug-inem do Visual Studia. Pokud by měl někdo zájem pomoci a
třeba u toho rovnou napsat, jak se mu povedlo ten plug-in udělat, má zde
příležitost. Dále musíme soubor s texturou nahrát do složky Content.
Texturu také naleznete v archivu, ale tentokráte již na svém místě ve
složce Content. Nemusíme ji vkládat přes Visual Studio, protože je na něj
odkaz má náš model a XNA
si jej vyzvedne samo. Pokud nyní
projekt zkompilujeme, vše se již povede bez chyb a my můžeme pokračovat
dále. Vytvoříme si pole typu Matrix
, kde budeme skladovat
transformace dané přímo modelovacím programem.
Matrix[] transforms;
Ano slyšíte dobře, transformace, není to nic jiného než matice
World
, která je ovšem v tomto případě daná přímo z
modelovacího programu. Jedná se opět o natočení modelu, změnu měřítka a
podobně, ale tentokrát ne od nás, ale od modelovacího programu. Přesuneme
se do metody LoadContent
, kde právě vytvořené pole
inicializujeme a naplníme hodnotami:
transforms=new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(transforms);
Dále si připravíme objekt pro samotné vykreslení. Stejně jako u
trojúhelníku i zde musíme nastavit parametry efektu (shaderu, zvykejte si i
na tento pojem) a to konkrétně všechny tři matice. Jenomže model není tak
snadná záležitost jako jeden trojúhelník. Jistě, skládá se z nich také,
ale jeho struktura je složitější. XNA
rozlišuje takzvané
meshe, reprezentováno třídou ModelMesh
. Pod tímto pojmem si lze
představit jeden souvislý povrch modelu. Třeba hlavu postavy. Dále
každý/á mesh má své části. Zde nazvané meshpart, a to ve třídě
ModelMeshPart
. Povětšinou má každá mesh pouze jednu část.
Záleží ale na její komplikovanosti. Právě tyto části v sobě obsahují
již připravené efekty, které čekají na nastavení hodnot. Nastavíme tedy
všude hodnoty matic View
, Projection
a
World
a povolíme jakési základní osvětlení:
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(); } }
Jistě, není to optimální řešení. Matice lze vypočítat i mimo cyklus
a pak jen nastavovat. Povšimněte si nastavení matice World
. Zde
používáme hodnoty z připraveného pole. Nyní se přemístíme do metody
Draw
. Jak vykreslit model? Máme hned několik možností. Je ale
dobré se již nyní naučit tuto, protože se nám bude v pozdějších dílech
více hodit:
foreach (ModelMesh mesh in model.Meshes){ mesh.Draw(); }
To je vše. Nic moc těžkého nebo náročného. Pokud nyní program spustíte, měli byste vidět nádherný perský koberec od Junita (tímto děkuji, že mi ten model půjčil, pamatuj na trojku!!).
Skvěle. A jelikož se toho zdá být málo tak přidáme jednoduchou animaci. Vytvoříme si proměnnou s maticí pro rotaci:
Matrix rotace;
V metodě Initialize
ji nastavíme na výchozí hodnotu:
rotace = Matrix.Identity;
Matice Identity
(česky jednotková) je taková matice, která
má na své hlavní diagonále samé jedničky. Je to jediná matice, se kterou
když vynásobíme matici jinou, tak výsledek je stejný jako předtím. V
metodě Update
přidáme zvětšení rotace o nějaký malý úhel.
Otáčíme kolem osy Y:
rotace *= Matrix.CreateRotationY(0.01f);
Násobíme předešlou matici maticí novou, tím dosáhneme toho, že se
úhly sečtou. Pak již jen v metodě Draw
nastavíme nový
parametr do World
matice a můžeme model opět vykreslit:
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šimněte si, že násobíme matici World
z editoru a maticí
naší. Nikoliv obráceně. Jinak by výsledek nebyl ten, který požadujeme.
Schválně si to zkuste. Násobení matic není komutativní tedy A*B není to
samé jako B*A. Uvedený způsob animací není ovšem optimální. Hodně
záleží na FPS, jak se z tohoto problému vymanit si ukážeme zase někdy
příště.
Tak a to je pro dnešek vše. Opět se těším na reakce a komentáře, které zatím přicházejí jen když si o ně řeknu. Jako úkol si zkuste s pomocí návodů z 2D série napsat jednoduché ovládání otáčení modelu pomocí třeba šipek. Příště se podíváme na... a víte co nechte se překvapit.
V následující lekci, XNA tvorba ve 3D - Engine poprvé, si napíšeme základ našeho vlastního herního 3D enginu.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 394x (1.58 MB)
Aplikace je včetně zdrojových kódů v jazyce C#