DalÜφ skv∞l² tutorißl! NauΦφte se, jak nahrßt a zobrazit otexturovan² Milkshape3D model. Nezdß se to, ale asi nejvφce se budou hodit znalosti o prßci s dynamickou pam∞tφ a jejφm kopφrovßnφ z jednoho mφsta na druhΘ.
Zdrojov² k≤d tohoto projektu byl vyjmut z PortaLib3D, knihovny, kterou jsem napsal, abych lidem umo₧nil zobrazovat modely za pou₧itφ velmi malΘho mno₧stvφ dalÜφho k≤du. Abyste se na ni mohli opravdu spolehnout musφte nejd°φve v∞d∞t, co d∞lß a jak pracuje.
╚ßst PortaLib3D, uvedenß zde, si stßle zachovßvß m∙j copyright. To neznamenß, ₧e ji nesmφte pou₧φvat, ale ₧e p°i vlo₧enφ k≤du do svΘho projektu musφte uvΘst nßle₧it² credit. To je vÜe - ₧ßdnΘ velkΘ nßroky. Pokud byste cht∞li Φφst, pochopit a re-implementovat cel² k≤d (₧ßdnΘ kopφrovat vlo₧it!), budete uvoln∞ni ze svΘ povinnosti. Pak je to vßÜ v²tvor. Poj∩me se ale podφvat na n∞co zajφmav∞jÜφho.
Model, kter² pou₧φvßme v tomto projektu, pochßzφ z Milkshape3D. Je to opravdu kvalitnφ balφk pro modelovßnφ, kter² zahrnuje vlastnφ file-formßt. M²m dalÜφm plßnem je implementovat Anim8or (http://www.anim8or.com/), souborov² reader. Je free a umφ Φφst samoz°ejm∞ i 3DS. NicmΘn∞ formßt souboru nenφ tφm hlavnφm pro loading model∙. Nejd°φve se musφ vytvo°it vlastnφ struktury, kterΘ jsou schopny pojmout data.
Prvnφ ze vÜeho deklarujeme obecnou t°φdu Model, kterß je kontejnerem pro vÜechna data.
class Model// ObecnΘ ·lo₧iÜt∞ dat (abstraktnφ t°φda)
{
public:
Ze vÜeho nejd∙le₧it∞jÜφ jsou samoz°ejm∞ vertexy. Pole t°φ desetinn²ch hodnot m_location reprezentuje jednotlivΘ x, y, z sou°adnice. Prom∞nnou m_boneID budeme v tomto tutorißlu ignorovat. Jejφ Φas p°ijde a₧ v dalÜφm p°i kosternφ animaci.
struct Vertex// Struktura vertexu
{
float m_location[3];// X, y, z sou°adnice
char m_boneID;// Pro skeletßlnφ animaci
};
VÜechny vertexy pot°ebujeme seskupit do troj·helnφk∙. Pole m_vertexIndices obsahuje t°i indexy do pole vertex∙. Touto cestou bude ka₧d² vertex ulo₧en v pam∞ti pouze jednou. V polφch m_s a m_t jsou texturovΘ koordinßty ka₧dΘho vrcholu. Poslednφ atribut definuje t°i normßlovΘ vektory pro sv∞tlo.
struct Triangle// Struktura troj·helnφku
{
int m_vertexIndices[3];// T°i indexy do pole vertex∙
float m_s[3], m_t[3];// TexturovΘ koordinßty
float m_vertexNormals[3][3];// T°i normßlovΘ vektory
};
DalÜφ struktura popisuje mesh modelu. Mesh je skupina troj·helnφk∙, na kterΘ je aplikovßn stejn² materißl a textura. Skupiny mesh∙ dohromady tvo°φ cel² model. Stejn∞ jako troj·helnφky obsahovaly pouze indexy na vertexy, budou i meshe obsahovat pouze indexy na troj·helnφky. Proto₧e neznßme jejich p°esn² poΦet, musφ b²t pole dynamickΘ. T°etφ prom∞nnß je op∞t indexem, tentokrßt do materißl∙ (textura, osv∞tlenφ).
struct Mesh//Mesh modelu
{
int *m_pTriangleIndices;// Indexy do troj·helnφk∙
int m_numTriangles;// PoΦet troj·helnφk∙
int m_materialIndex;// Index do materißl∙
};
Ve struktu°e Material jsou ulo₧enΘ standardnφ koeficienty sv∞tla, ve stejnΘm formßtu jako pou₧φvß OpenGL: okolnφ (ambient), rozpt²lenΘ (diffuse), odra₧enΘ (specular), vyza°ujφcφ (emissive) a lesklost (shininess). dßle obsahuje objekt textury a souborovou cestu k textu°e, aby mohla b²t znovu nahrßna, kdy₧ je ukonΦen kontext OpenGL.
struct Material// Vlastnosti materißl∙
{
float m_ambient[4], m_diffuse[4], m_specular[4], m_emissive[4];// Reakce materißlu na sv∞tlo
float m_shininess;// Lesk materißlu
GLuint m_texture;// Textura
char *m_pTextureFilename;// Souborovß cesta k textu°e
};
Vytvo°φme prom∞nnΘ prßv∞ napsan²ch struktur ve form∞ ukazatel∙ na dynamickß pole, jejich₧ pam∞¥ alokuje funkce pro loading objekt∙. Musφme samoz°ejm∞ uklßdat i velikost polφ.
protected:
int m_numVertices;// PoΦet vertex∙
Vertex *m_pVertices;// DynamickΘ pole vertex∙
int m_numTriangles;// PoΦet troj·helnφk∙
Triangle *m_pTriangles;// DynamickΘ pole troj·helnφk∙
int m_numMeshes;// PoΦet mesh∙
Mesh *m_pMeshes;// DynamickΘ pole mesh∙
int m_numMaterials;// PoΦet materißl∙
Material *m_pMaterials;// DynamickΘ pole materißl∙
A koneΦn∞ metody t°φdy. Virtußlnφ Φlenskß funkce loadModelData() mß za ·kol nahrßt data ze souboru. P°i°adφme jφ nulu, aby nemohl b²t vytvo°en objekt t°φdy (abstraktnφ t°φda). Tato t°φda je zam²Ülena pouze jako ·lo₧iÜt∞ dat. VÜechny operace pro nahrßvßnφ majφ na starosti odvozenΘ t°φdy, kdy ka₧dß z nich umφ sv∙j vlastnφ formßt souboru. Celß hierarchie je vφce obecnß.
public:
Model();// Konstruktor
virtual ~Model();// Destruktor
virtual bool loadModelData(const char *filename) = 0;// Loading objektu ze souboru
Metoda reloadTextures() slou₧φ pro loading textur a jejich znovunahrßvßnφ, kdy₧ se ztratφ kontext OpenGL (nap°. p°i p°epnutφ z/do fullscreenu). Draw() vykresluje objekt. Tato funkce nemusφ b²t virtußlnφ, proto₧e defakto znßme vÜechny pot°ebnΘ informace o struktu°e objektu (vertexy, troj·helnφky...).
void reloadTextures();// Znovunahrßnφ textur
void draw();// Vykreslenφ objektu
};
Od t°φdy Model pod∞dφme t°φdu MilkshapeModel. P°epφÜeme v nφ metodu loadModelData().
class MilkshapeModel : public Model
{
public:
MilkshapeModel();// Konstruktor
virtual ~MilkshapeModel();// Destruktor
virtual bool loadModelData(const char *filename);// Loading objektu ze souboru
};
Nynφ nahrßvßnφ objekt∙. P°epφÜeme virtußlnφ funkci loadModelData() abstraktnφ t°φdy Model tak, aby ve t°φd∞ MilkShapeModel nahrßvala data ze souboru ve formßtu Milkshape3D. P°edßvßme jφ °et∞zec se jmΘnem souboru. Pokud vÜe prob∞hne v po°ßdku, funkce nastavφ datovΘ struktury a vrßtφ true.
bool MilkshapeModel::loadModelData(const char *filename)
{
Soubor otev°eme jako vstupnφ (ios::in), binßrnφ (ios::binary) a nebudeme ho vytvß°et (ios::nocreate). Pokud nebyl nalezen vrßtφ funkce false, aby indikovala error.
ifstream inputFile(filename, ios::in | ios::binary | ios::nocreate);// Otev°enφ souboru
if (inputFile.fail())// Poda°ilo se ho otev°φt?
return false;
Zjistφme velikost souboru v bytech a potom ho cel² naΦteme do pomocnΘho bufferu pBuffer.
// Velikost souboru
inputFile.seekg(0, ios::end);
long fileSize = inputFile.tellg();
inputFile.seekg(0, ios::beg);
byte *pBuffer = new byte[fileSize];// Alokace pam∞ti pro kopii souboru
inputFile.read(pBuffer, fileSize);// Vytvo°enφ pam∞¥ovΘ kopie souboru
inputFile.close();// Zav°enφ souboru
Deklarujeme pomocn² ukazatel pPtr, kter² ihned inicializujeme tak, aby ukazoval na stejnΘ mφsto jako pBuffer, tedy na zaΦßtek pam∞ti. Do hlaviΦky souboru pHeader ulo₧φme adresu hlaviΦky a zv∞tÜφme adresu v pPtr o velikost hlaviΦky.
Pozn.: Strukturu hlaviΦky a jφ podobnΘ jsem na zaΦßtku tutorißlu neuvßd∞l, proto₧e je budeme pou₧φvat jenom zde, v tΘto funkci. Pokud vßs p°eci zajφmajφ, stßhn∞te si zdrojov² k≤d. Jsou deklarovanΘ naho°e v souboru MilkshapeModel.cpp.
const byte *pPtr = pBuffer;// Pomocn² ukazatel na kopii souboru
MS3DHeader *pHeader = (MS3DHeader*)pPtr;// Ukazatel na hlaviΦku
pPtr += sizeof(MS3DHeader);// Posun za hlaviΦku
HlaviΦka p°φmo specifikuje formßt souboru. Ujistφme se, ₧e se jednß o platn² formßt, kter² umφme nahrßt.
// Nenφ Milkshape3D souborem
if (strncmp(pHeader->m_ID, "MS3D000000", 10) != 0)
{
delete [] pBuffer;// P°ekl.: Sma₧e kopii souboru !!!!!
return false;
}
// èpatnß verze souboru, t°φda podporuje pouze verze 1.3 a 1.4
if (pHeader->m_version < 3 || pHeader->m_version > 4)
{
delete [] pBuffer;// P°ekl.: Sma₧e kopii souboru !!!!!
return false;
}
NaΦteme vÜechny vertexy. Nejd°φve zjistφme jejich poΦet, alokujeme pot°ebnou pam∞¥ a p°esuneme pPtr na dalÜφ pozici. V cyklu prochßzφme jednotlivΘ vertexy. Nastavφme ukazatel pVertex na p°etypovan² pPtr a definujeme m_boneID. Nakonec zavolßme memcpy() pro zkopφrovßnφ hodnot a zv∞tÜφme pPtr.
int nVertices = *(word*)pPtr;// PoΦet vertex∙
m_numVertices = nVertices;// Nastavφ atribut t°φdy
m_pVertices = new Vertex[nVertices];// Alokace pam∞ti pro vertexy
pPtr += sizeof(word);// Posun za poΦet vertex∙
int i;//Pomocnß prom∞nnß
for (i = 0; i < nVertices; i++)// Nahrßvß vertexy
{
MS3DVertex *pVertex = (MS3DVertex*)pPtr;// Ukazatel na vertex
// NaΦtenφ vertexu
m_pVertices[i].m_boneID = pVertex->m_boneID;
memcpy(m_pVertices[i].m_location, pVertex->m_vertex, sizeof(float) * 3);
pPtr += sizeof(MS3DVertex);// Posun za tento vertex
}
Stejn∞ jako u vertex∙, tak i troj·helnφk∙ nejd°φve provedeme pot°ebnΘ operace pro alokaci pam∞ti. V cyklu prochßzφme jednotlivΘ troj·helnφky a inicializujeme je. VÜimn∞te si, ₧e v souboru jsou indexy vertex∙ ulo₧eny v poli word hodnot, ale v modelu kv∙li konzistentnosti a jednoduchosti pou₧φvßme datov² typ int. ╚φslo se implicitn∞ p°etypuje.
int nTriangles = *(word*)pPtr;// PoΦet troj·helnφk∙
m_numTriangles = nTriangles;// Nastavφ atribut t°φdy
m_pTriangles = new Triangle[nTriangles];// Alokace pam∞ti pro troj·helnφky
pPtr += sizeof(word);// Posun za poΦet troj·helnφk∙
for (i = 0; i < nTriangles; i++)// NaΦφtß troj·helnφky
{
MS3DTriangle *pTriangle = (MS3DTriangle*)pPtr;// Ukazatel na troj·helnφk
// NaΦtenφ troj·helnφku
int vertexIndices[3] = { pTriangle->m_vertexIndices[0], pTriangle->m_vertexIndices[1], pTriangle->m_vertexIndices[2] };
VÜechna Φφsla v poli t jsou nastavena na 1.0 mφnus originßl. To proto, ₧e OpenGL pou₧φvß poΦßtek texturovacφho sou°adnicovΘho systΘmu vlevo dole, narozdφl od Milkshape, kterΘ ho mß vlevo naho°e. OdeΦtenφm od jedniΦky, y sou°adnici invertujeme. VÜe ostatnφ by m∞lo b²t bez problΘm∙.
float t[3] = { 1.0f-pTriangle->m_t[0], 1.0f-pTriangle->m_t[1], 1.0f-pTriangle->m_t[2] };
memcpy(m_pTriangles[i].m_vertexNormals, pTriangle->m_vertexNormals, sizeof(float)*3*3);
memcpy(m_pTriangles[i].m_s, pTriangle->m_s, sizeof(float)*3);
memcpy(m_pTriangles[i].m_t, t, sizeof(float)*3);
memcpy(m_pTriangles[i].m_vertexIndices, vertexIndices, sizeof(int)*3);
pPtr += sizeof(MS3DTriangle);// Posun za tento troj·helnφk
}
Nahrajeme struktury mesh. V Milkshape3D jsou takΘ naz²vßny groups - skupiny. V ka₧dΘ se liÜφ poΦet troj·helnφk∙, tak₧e nem∙₧eme naΦφst ₧ßdnou standardnφ strukturu. Namφsto toho budeme dynamicky alokovat pam∞¥ pro indexy troj·helnφk∙ a v ka₧dΘm pr∙chodu je naΦφtat.
int nGroups = *(word*)pPtr;// PoΦet mesh∙
m_numMeshes = nGroups;// Nastavφ atribut t°φdy
m_pMeshes = new Mesh[nGroups];// Alokace pam∞ti pro meshe
pPtr += sizeof(word);// Posun za poΦet mesh∙
for (i = 0; i < nGroups; i++)// NaΦφtß meshe
{
pPtr += sizeof(byte);// Posun za flagy
pPtr += 32;// Posun za jmΘno
word nTriangles = *(word*)pPtr;// PoΦet troj·helnφk∙ v meshi
pPtr += sizeof(word);// Posun za poΦet troj·helnφk∙
int *pTriangleIndices = new int[nTriangles];// Alokace pam∞ti pro indexy troj·helnφk∙
for (int j = 0; j < nTriangles; j++)// NaΦφtß indexy troj·helnφk∙
{
pTriangleIndices[j] = *(word*)pPtr;// P°i°adφ index troj·helnφku
pPtr += sizeof(word);// Posun za index troj·helnφku
}
char materialIndex = *(char*)pPtr;// NaΦte index materißlu
pPtr += sizeof(char);// Posun za index materißlu
m_pMeshes[i].m_materialIndex = materialIndex;// Index materißlu
m_pMeshes[i].m_numTriangles = nTriangles;// PoΦet troj·helnφk∙
m_pMeshes[i].m_pTriangleIndices = pTriangleIndices;// Indexy troj·helnφk∙
}
Poslednφ, co naΦφtßme jsou informace o materißlech.
int nMaterials = *(word*)pPtr;// PoΦet materißl∙
m_numMaterials = nMaterials;// Nastavφ atribut t°φdy
m_pMaterials = new Material[nMaterials];// Alokace pam∞ti pro materißly
pPtr += sizeof(word);// Posun za poΦet materißl∙
for (i = 0; i < nMaterials; i++)// Prochßzφ materißly
{
MS3DMaterial *pMaterial = (MS3DMaterial*)pPtr;// Ukazatel na materißl
// NaΦte materißl
memcpy(m_pMaterials[i].m_ambient, pMaterial->m_ambient, sizeof(float)*4);
memcpy(m_pMaterials[i].m_diffuse, pMaterial->m_diffuse, sizeof(float)*4);
memcpy(m_pMaterials[i].m_specular, pMaterial->m_specular, sizeof(float)*4);
memcpy(m_pMaterials[i].m_emissive, pMaterial->m_emissive, sizeof(float)*4);
m_pMaterials[i].m_shininess = pMaterial->m_shininess;
Alokujeme pam∞¥ pro °et∞zec jmΘna souboru textury a zkopφrujeme ho.
// Alokace pro jmΘno souboru textury
m_pMaterials[i].m_pTextureFilename = new char[strlen(pMaterial->m_texture)+1];
// Zkopφrovßnφ jmΘna souboru
strcpy(m_pMaterials[i].m_pTextureFilename, pMaterial->m_texture);
// Posun za materißl
pPtr += sizeof(MS3DMaterial);
}
Nakonec loadujeme textury objektu, uvolnφme pam∞¥ kopie souboru a vrßtφme true, abychom oznßmili ·sp∞ch celΘ akce.
reloadTextures();// Nahraje textury
delete [] pBuffer;// Sma₧e kopii souboru
return true;// Model byl nahrßn
}
Nynφ jsou ΦlenskΘ prom∞nnΘ t°φdy Model vypln∞nΘ. Zb²vß jeÜt∞ nahrßt textury. V cyklu prochßzφme vÜechny materißly a testujeme, jestli je °et∞zec se jmΘnem textury delÜφ ne₧ nula. Pokud ano nahrajeme texturu pomocφ standardnφ NeHe funkce. Pokud ne p°i°adφme textu°e nulu jako indikaci, ₧e neexistuje.
void Model::reloadTextures()// Nahrßnφ textur
{
for (int i = 0; i < m_numMaterials; i++)// JednotlivΘ materißly
{
if (strlen(m_pMaterials[i].m_pTextureFilename) > 0)// Existuje °et∞zec s cestou
{
// Nahraje texturu
m_pMaterials[i].m_texture = LoadGLTexture(m_pMaterials[i].m_pTextureFilename);
}
else
{
// Nulou indikuje, ₧e materißl nemß texturu
m_pMaterials[i].m_texture = 0;
}
}
}
M∙₧eme zaΦφt vykreslovat model. Dφky uspo°ßdßnφ do struktur to nenφ nic slo₧itΘho. Ze vÜeho nejd°φve ulo₧φme atribut, jestli je zapnutΘ nebo vypnutΘ texturovßnφ. Na konci funkce ho budeme moci obnovit.
void Model::draw()
{
GLboolean texEnabled = glIsEnabled(GL_TEXTURE_2D);// Ulo₧φ atribut
Ka₧d² mesh renderujeme samostatn∞, proto₧e mesh seskupuje vÜechny troj·helnφky se stejn²mi vlastnostmi. StaΦφ jedno hromadnΘ nastavenφ OpenGL pro velkou skupinu polygon∙, namφsto mnohem mΘn∞ efektivnφmu: nastavit vlastnosti pro troj·helnφk - vykreslit troj·helnφk. S meshi postupujeme takto: nastavit vlastnosti - vykreslit vÜechny troj·helnφky s t∞mito vlastnostmi.
for (int i = 0; i < m_numMeshes; i++)// Meshe
{
M_pMeshes[i] pou₧ijeme jako referenci na aktußlnφ mesh. Ka₧d² z nich mß vlastnφ materißlovΘ vlastnosti, podle kter²ch nastavφme OpenGL. Pokud se materialIndex rovnß -1, znamenß to, ₧e mesh nenφ definovßn. V takovΘm p°φpad∞ z∙staneme u implicitnφch nastavenφ OpenGL. Texturu zvolφme a zapneme pouze tehdy, pokud je v∞tÜφ ne₧ nula. P°i jejφm loadingu jsme nadefinovali, ₧e pokud neexistuje nastavφme ji na nulu. Vypnutφ texturingu je tedy logick²m krokem. Pokud materißl meshe neexistuje, texturovßnφ takΘ vypneme, proto₧e nemßme kde vzφt texturu.
int materialIndex = m_pMeshes[i].m_materialIndex;// Index
if (materialIndex >= 0)// Obsahuje mesh index materißlu?
{
// Nastavφ OpenGL
glMaterialfv(GL_FRONT, GL_AMBIENT, m_pMaterials[materialIndex].m_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, m_pMaterials[materialIndex].m_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, m_pMaterials[materialIndex].m_specular);
glMaterialfv(GL_FRONT, GL_EMISSION, m_pMaterials[materialIndex].m_emissive);
glMaterialf(GL_FRONT, GL_SHININESS, m_pMaterials[materialIndex].m_shininess);
if (m_pMaterials[materialIndex].m_texture > 0)// Obsahuje materißl texturu?
{
glBindTexture(GL_TEXTURE_2D, m_pMaterials[materialIndex].m_texture);
glEnable(GL_TEXTURE_2D);
}
else// Bez textury
{
glDisable(GL_TEXTURE_2D);
}
}
else// Bez materißlu nem∙₧e b²t ani textura
{
glDisable(GL_TEXTURE_2D);
}
P°i vykreslovßnφ prochßzφme nejd°φve vÜechny troj·helnφky meshe a potom ka₧d² z jeho vrchol∙. Specifikujeme normßlov² vektor a texturovΘ koordinßty.
glBegin(GL_TRIANGLES);// ZaΦßtek troj·helnφk∙
{
for (int j = 0; j < m_pMeshes[i].m_numTriangles; j++)// Troj·helnφky v meshi
{
int triangleIndex = m_pMeshes[i].m_pTriangleIndices[j];// Index
const Triangle* pTri = &m_pTriangles[triangleIndex];// Troj·helnφk
for (int k = 0; k < 3; k++)// Vertexy v troj·helnφku
{
int index = pTri->m_vertexIndices[k];// Index vertexu
glNormal3fv(pTri->m_vertexNormals[k]);// Normßla
glTexCoord2f(pTri->m_s[k], pTri->m_t[k]);// Texturovacφ sou°adnice
glVertex3fv(m_pVertices[index].m_location);// Sou°adnice vertexu
}
}
}
glEnd();// Konec kreslenφ
}
Obnovφme atribut OpenGL.
// Obnovenφ nastavenφ OpenGL
if (texEnabled)
{
glEnable(GL_TEXTURE_2D);
}
else
{
glDisable(GL_TEXTURE_2D);
}
}
Jedin²m dalÜφm k≤dem ve t°φd∞ Model, kter² stojφ za pozornost je konstruktor a destruktor. Konstruktor inicializuje vÜechny ΦlenskΘ prom∞nnΘ na nulu nebo v p°φpad∞ ukazatel∙ na NULL. M∞jte na pam∞ti, ₧e pokud zavolßte funkci loadModelData() dvakrßt pro jeden objekt, nastanou ·niky pam∞ti! Pam∞¥ se toti₧ uvol≥uje a₧ v destruktoru.
Model::Model()// Konstruktor
{
m_numMeshes = 0;
m_pMeshes = NULL;
m_numMaterials = 0;
m_pMaterials = NULL;
m_numTriangles = 0;
m_pTriangles = NULL;
m_numVertices = 0;
m_pVertices = NULL;
}
Model::~Model()// Destruktor
{
int i;
for (i = 0; i < m_numMeshes; i++)
{
delete[] m_pMeshes[i].m_pTriangleIndices;
}
for (i = 0; i < m_numMaterials; i++)
{
delete[] m_pMaterials[i].m_pTextureFilename;
}
m_numMeshes = 0;
if (m_pMeshes != NULL)
{
delete[] m_pMeshes;
m_pMeshes = NULL;
}
m_numMaterials = 0;
if (m_pMaterials != NULL)
{
delete[] m_pMaterials;
m_pMaterials = NULL;
}
m_numTriangles = 0;
if (m_pTriangles != NULL)
{
delete[] m_pTriangles;
m_pTriangles = NULL;
}
m_numVertices = 0;
if (m_pVertices != NULL)
{
delete[] m_pVertices;
m_pVertices = NULL;
}
}
Vysv∞tlili jsme si t°φdu Model, zbytek u₧ bude velice jednoduch². Naho°e v souboru Lesson32.cpp deklarujeme ukazatel na model a inicializujeme ho na NULL.
Model *pModel = NULL;// Ukazatel na model
Jeho data nahrajeme a₧ ve funkci WinMain(). Loading NIKDY nevklßdejte do InitGL(), proto₧e se volß v₧dycky, kdy₧ u₧ivatel zm∞nφ m≤d fullscreen/okno. P°i tΘto akci se ztrßcφ a znovu vytvß°φ OpenGL kontext, ale data modelu se nemusφ (a kv∙li ·nik∙m pam∞ti dokonce nesmφ) reloadovat. Z∙stßvajφ nedotΦenß. StaΦφ znovu nahrßt textury, kterΘ jsou na OpenGL zßvislΘ. Je-li ve scΘn∞ vφce model∙, musφ se reloadTextures() volat zvlßÜ¥ pro ka₧d² objekt t°φdy. Pokud se stane, ₧e budou modely najednou bφlΘ, znamenß to, ₧e se textury nenahrßly sprßvn∞.
// ZaΦßtek funkce WinMain()
pModel = new MilkshapeModel();// Alokace pam∞ti pro model
if (pModel->loadModelData("data/model.ms3d") == false)// Pokusφ se nahrßt model
{
MessageBox(NULL, "Couldn't load the model data\\model.ms3d", "Error", MB_OK | MB_ICONERROR);
return 0;// Model se nepoda°ilo nahrßt - program se ukonΦφ
}
// ZaΦßtek funkce InitGL()
pModel->reloadTextures();// Nahrßnφ textur modelu
Poslednφ, co popφÜeme je DrawGLScene(). Namφsto klasick²ch glTranslatef() a glRotatef() pou₧ijeme funkci gluLookAt(). Prvnφmi t°emi parametry umφs¥uje kameru na pozici, prost°ednφ t°i sou°adnice urΦujφ st°ed scΘny a poslednφ t°i definujφ vektor sm∞°ujφcφ vzh∙ru. V naÜem p°φpad∞ se dφvßme z bodu (75, 75, 75) na bod (0, 0, 0). Model tedy bude vykreslen kolem sou°adnic (0, 0, 0), pokud p°ed kreslenφm neprovedeme translaci. Osa y sm∞°uje vzh∙ru. Aby se gluLookAt() chovala tφmto zp∙sobem, musφ b²t volßna jako prvnφ po glLoadIdentity().
int DrawGLScene(GLvoid)// Rendering scΘny
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Sma₧e buffery
glLoadIdentity();// Reset matice
gluLookAt(75,75,75, 0,0,0, 0,1,0);// P°esun kamery
Aby byl v²sledek trochu zajφmav∞jÜφ rotujeme modelem kolem osy y.
glRotatef(yrot, 0.0f, 1.0f, 0.0f);// Rotace na ose y
Pro rendering modelu pou₧ijeme jeho vlastnφ funkce. Vykreslφ se vycentrovan² okolo st°edu, ale pouze tehdy, ₧e i v Milkshape 3D byl modelovßn okolo st°edu. Pokus s nφm budete chtφt rotovat, posunovat nebo m∞nit velikost, zavolejte odpovφdajφcφ OpenGL funkce. Pro otestovßnφ si zkuste vytvo°it vlastnφ model a nahrajte ho do programu. Funguje?
pModel->draw();// Rendering modelu
yrot += 1.0f;// OtßΦenφ scΘny
return TRUE;
}
A co dßl? Plßnuji dalÜφ tutorißl pro NeHe, ve kterΘm rozÜφ°φme t°φdu tak, aby umo₧≥ovala animaci objektu pomocφ jeho kostry (skeletal animation). Mo₧nß takΘ naprogramuji dalÜφ t°φdy loader∙ - program bude schopen nahrßt vφce r∙zn²ch formßt∙. Krok ke skeletßlnφ animaci nenφ a₧ zase tak velk², jak se m∙₧e zdßt, aΦkoli matematika bude o stupe≥ slo₧it∞jÜφ. Pokud jeÜt∞ nerozumφte maticφm a vektor∙m, je Φas se na n∞ trochu podφvat.
napsal: Brett Porter
p°elo₧il: Michal Turek - Woq
Brett Porter se narodil v Austrßlii, studoval na WollogongskΘ Univerzit∞. Nedßvno absolvoval na BCompSc A BMath (BSc - bakalß° p°φrodnφch v∞d). Programovat zaΦal p°ed dvanßcti lety v Basicu na "klonu" Commodore 64 zvanΘm VZ300, ale brzy p°eÜel na Pascal, Intel Assembler, C++ a Javu. P°ed n∞kolika lety zaΦal pou₧φvat OpenGL.