|
Kamera, procházení scénou
|
|
|
Funkce CalculateFrameRate() počítá FPS.
|
|
void CalculateFrameRate(void) // Vypočítej FPS
{
static int frames_per_second = 0; // Hodnota FPS
static float last_time = 0.0f; // Hodnota času posledního snímku
static char str_frame_rate[50] = {0}; // String hodnota FPS
float current_time = GetTickCount() * 0.001f; // Zjisti aktuální čas v milisekundách a převeď na sekundy
++frames_per_second; // Přičti snímek
if( current_time - last_time > 1.0f ) // Jestliže rozdíl hodnot aktuálního času a času posledního snímku je větší než 1 (1 sekunda)
{
last_time = current_time; // Tak předej hodnotu aktuálního času, času posledního snímku
fps = frames_per_second; // Předej hodnotu frames_per_second globální proměnné fps
frames_per_second = 0; // Vynuluj hodnotu FPS
}
}
|
Výsledek pošle globální proměnné fps, kterou vykreslí na obrazovku funkce
DrawFonts(), když hodnotu fps vložíme do funkce jako konstantu. Tato funkce může mimochodem
vykreslit na obrazovku jakýkoli text.
|
|
void DrawFonts(const char *fmt, ...) // Vykresli text
{
char text[256]; // String o 256 znacích
va_list ap; // Ukazatel na List argumentů, které chceme vložit do řetězce
if (fmt == NULL) // Jestliže fmt neobsahuje žádný text
return; // Tak nic nedělej a odejdi z funkce
va_start(ap, fmt); // Zkus vytvořit kopii fmt v Listu ap
vsprintf(text, fmt, ap); // Konvertuj znaky na aktuální čísla znaků
va_end(ap); // Vymaž ukazatel na List argumentů
glPushAttrib(GL_LIST_BIT); // Začátek Listu Bitů
glListBase(base - 32); // Nastav znaky od znaku 32 výš, jako základní Display List
glCallLists(strlen(text), GL_UNSIGNED_BYTE, text); // Vykresli Display List
glPopAttrib(); // Konec Listu Bitů
}
|
Tato funkce vykresluje znaky, které jsou uloženy v hodnotách base Display Listu.
Tyto hodnoty musíme nastavit ve funkci BuildFont()
|
|
void BuildFont(void) // Vytvoř font
{
HFONT font; // ID - identifikátor Windows Fonu
base = glGenLists(96); // Najdi 96 prázdných Listů
font = CreateFont( -12, // Velikost
0, // Šířka
0, // Úhel vykreslení
0, // Orientace úhlu
FW_BOLD, // Tučný
FALSE, // Italic
FALSE, // Nepodtržený
FALSE, // Nepřeškrtlý
ANSI_CHARSET, // Kódování
OUT_TT_PRECIS, // TrueType
CLIP_DEFAULT_PRECIS, // Základní ořezávání
ANTIALIASED_QUALITY, // Výstupní kvalita
FF_DONTCARE|DEFAULT_PITCH, // Styl
"Courier New"); // Jméno
SelectObject(hDC, font); // Vyber objekt
wglUseFontBitmaps(hDC, 32, 96, base); // Vytvoř bitmaty znakové sady od 32 do 96 a ulož do base
DeleteObject(font); // Vymaž font
}
|
Funkce BuildFont() je volána jen jednou funkcí InitScene().
|
|
int InitScene(void) // Načti scénu
{
if (!Texture1.LoadGLTextures("Textures/picture01.bmp")) // Zkus načíst texturu
{
return FALSE; // Jestliže se to nepodaří, tak opusť fukci
}
if (!Texture1.LoadGLTextures("Textures/picture02.bmp")) // Zkus načíst texturu
{
return FALSE; // Jestliže se to nepodaří, tak opusť fukci
}
LoadAllObjects(); // Načti objekty ze souboru
InitCube(); // Načti krychli
BuildFont(); // Vytvoř bitmap fonty
return TRUE; // Vrať TRUE pokud vše proběhlo OK
}
|
Funkce CalculateFrameRate() a DrawFonts() je volána funkcí DrawFps().
Tato funkce vykreslí text žlutý a průhledný, aby byl lépe vidět.
|
|
void DrawFps(void)
{
CalculateFrameRate(); // Vypočítej FPS
glColor3f(1,1,0); // Barvu textu nastav na žlutou
glEnable(GL_BLEND); // Zapni průhlednost
glBlendFunc(GL_ONE, GL_ONE); // Vše, co je černé je průhledné
glLoadIdentity(); // Resetuj prostorovou matici
glTranslatef(0.0f, 0.0f, -0.1f); // Přesuň text o 0,1 před kameru
glRasterPos2f(-0.054f, 0.039f); // Pozici textu nastav do levého horního rohu render okna
DrawFonts("fps %1i",fps); // Napiš text do render okna
glDisable(GL_BLEND); // Vypni průhlednost
glColor3f(1,1,1); // Barvu nastav zpět na bílou
}
|
Funkce DrawFps() je volána v nekonečné smyčce fukcí DrawScene().
|
|
int DrawScene(void) // Vykresli scénu
{
Camera1.Look(); // Pohybuj a rotuj kamerou
DrawCube(); // Vykresli krychli
DrawFps(); // Vykresli FPS
return TRUE; // Vrať TRUE pokud vše proběhlo OK
}
|
Soubor objektu cube.txt má tuto podobu
|
|
Vertices: 8
-1.0 -1.0 1 v0
-1.0 1.0 1 v1
1.0 -1.0 1 v2
1.0 1.0 1 v3
1.0 -1.0 -1 v4
1.0 1.0 -1 v5
-1.0 -1.0 -1 v6
-1.0 1.0 -1 v7
|
Hodnota za Vertices: nám zadává, kolik vertexů obsahuje objekt. Protože jde o krychli,
tak máme 8 vrcholů Další hodnoty jsou
X, Y, Z pozice jednotlivých vrcholů. v0 - v7 je jen informativní text,
který se nenačítá. To samé platí o textu Vertices:.
Objekt cube2.txt má stejnou strukturu s jinými hodnotami.
Pro načítání objektů a jejich vykreslování si vytvoříme tyto datové typy
|
|
//----------------------------------------------------------------------------------------------------//
//-------- Definuj struktury
//----------------------------------------------------------------------------------------------------//
typedef struct // Struktura 3D bodů
{
GLfloat x, y, z; // Bod x, y, z
} TPoint3d;
//----------------------------------------------------------------------------------------------------//
typedef struct // Struktura objektů
{
int verts; // Počet vrcholů
TPoint3d *points; // Vrchol je složen z bodu x, y, z
} TGLObject;
//----------------------------------------------------------------------------------------------------//
|
Vytvoříme si objekty typu TGLObject
|
|
TGLObject cube1, cube2; //Objekt cube1, cube2
|
A budeme volat funkci pro načítání objektů ze souborů cube.txt, cube2.txt
|
|
void LoadAllObjects(void) // Načti objekty
{
cube1 = LoadObject("Objects/cube.txt", &cube1); // Načti hodnoty uložené v souboru cube.txt a předej je objektu cube1
cube2 = LoadObject("Objects/cube2.txt", &cube2); // Načti hodnoty uložené v souboru cube2.txt a předej je objektu cube2
}
|
Funkce pro načítání vypadá takto
|
|
TGLObject LoadObject(char *name, TGLObject *k) // Načti objekt se souboru (name)
{
int ver; // Pomocná proměnná pro počet vrcholů objektu
GLfloat rx,ry,rz; // Pomocná proměnná pro X, Y, Z pozici vrcholu objektu
FILE *filein; // Definice souboru, ktery budeme číst
char oneline[255]; // Definice řádku souboru s maximálně 255 znaky
if ((filein = fopen(name, "rt"))== NULL) // Zkus otevřít soubor pro čtení v textovem módu "rt"
{
// Jestli se to nezdaří, vyhoď chybovou hlášku
MessageBox(NULL,"Cannot open input file","Error",MB_OK | MB_ICONSTOP);
}
else // Jinak pokračuj v práci s načteným souborem
{
ReadLine(filein,oneline); // Volej funkci pro načtení řádky ze souboru
sscanf(oneline, "Vertices: %d\n", &ver); // Přečti z první řádky počet vrcholů a výsledek předej proměnné ver
k->points = new TPoint3d[ver]; // Dynamicky alokuj paměť pro počet vrcholů objektu
k->verts = ver; // Načtenou hodnotu počtu vrcholů předej vrcholům objektu
for (int i=0;i<ver
;i++) // Cykluj podle počtu vrcholů
{
ReadLine(filein,oneline); // Čti řádek po řádku
sscanf(oneline, "%f %f %f", &rx, &ry, &rz); // Najdi v řádce tři čísla typu float a předej je hodnotám rx, ry, rz
k->points[i].x = rx; // Předej bodu X (aktuálniho vrcholu) hodnotu rx
k->points[i].y = ry; // Předej bodu Y (aktuálního vrcholu) hodnotu ry
k->points[i].z = rz; // Předej bodu Z (aktuálního vrcholu) hodnotu rz
}
fclose(filein); // Zavři soubor
}
return *k; // Vrať funkci hodnoty objektu
}
|
Tato funkce vrátí hodnoty vrcholů k, pomocí kterých vykreslujeme objekty (krychle)
|
|
void InitCube(void) // Vykresli krychli
{
glNewList(1,GL_COMPILE);
...
...
...
// Čelní stěna krychle
glBegin(GL_TRIANGLE_FAN); // Začni vykreslovat čtverec
glTexCoord2f(0.0f, 1.0f); glVertex3d( cube1.points[0].x, cube1.points[0].y, cube1.points[0].z); // Horní levý bod
glTexCoord2f(0.0f, 0.0f); glVertex3d( cube1.points[1].x, cube1.points[1].y, cube1.points[1].z); // Dolní levý bod
glTexCoord2f(1.0f, 0.0f); glVertex3d( cube1.points[2].x, cube1.points[2].y, cube1.points[2].z); // Dolní pravý bod
glTexCoord2f(1.0f, 1.0f); glVertex3d( cube1.points[3].x, cube1.points[3].y, cube1.points[3].z); // Horní pravý bod
glEnd(); // Ukonči vykreslování čtverce
...
...
...
}
|
Funkce LoadObject() používá pro načtení řádky ze souboru funkci ReadLine()
|
|
void ReadLine(FILE *f,char *s) // Čti řádek ze souboru f a ukládej do řetězce s
{
do // Cykluj
{
fgets(s, 255, f); // Ćti znak po znaku maximálně však 255
} while ((s[0] == '/') || (s[0] == '\n')); // Dokud se nedostaneš na konec řádky
return; // Vrať funkci hodnotu načteného řádku
}
|
Funkce pro načítání objektů se samozřejmě provádějí jen jednou, při startu aplikace.
Při startu aplikace také musíme zapnout DEPTH buffer glEnable(GL_DEPTH_TEST);.
Pokud bychom nechali vypnutý DEPTH buffer, objekty by se vykreslovali v tom pořadí, v jakém
jsou volány, takže by se stalo, že by se zadní stěna krychle vykreslila před přední.
|
|
int InitScene(void) // Načti scénu
{
if (!Texture1.LoadGLTextures("Textures/picture01.bmp")) // Zkus načíst texturu
{
return FALSE; // Jestliže se to nepodaří, tak opusť fukci
}
if (!Texture1.LoadGLTextures("Textures/picture02.bmp")) // Zkus načíst texturu
{
return FALSE; // Jestliže se to nepodaří, tak opusť fukci
}
LoadAllObjects(); // Načti objekty ze souboru
InitCube(); // Načti krychli
BuildFont(); // Vytvoř bitmap fonty
return TRUE; // Vrať TRUE pokud vše proběhlo OK
}
|
Pro načítání textur ze souboru *.bmp jsem vytvořil třídu KTexture
|
|
class KTexture // Třída textur
{
public:
KTexture() {} // Konstruktor textury
// Proměnné
int Number; // Počet textur
unsigned int TextureList[256]; // Definice proměnné pro 2d texturu
// Funkce
int LoadGLTextures(char *Filename); // Načítání textury *.bmp
};
|
Funkce pro načítání textury LoadGLTextures() vypadá takto:
|
|
int KTexture::LoadGLTextures(char *Filename) // Načti obrázek *.bmp a převeď ho na texturu
{
int Status=FALSE; // Kontrolu stavu načítaní nastav na FALSE
AUX_RGBImageRec *TextureImage[1]; // Alokuj paměť pro obraz textury
memset(TextureImage,0,sizeof(void *)*1); // Nastav ukazatel na NULL
// Jestliže pude načíst obrázek
if (TextureImage[0]=LoadBMP(Filename))
{
Status=TRUE; // Kontrolu stavu načítání nastav na TRUE
glGenTextures(1, &TextureList[Number]); // Vytvoř texturový List
// Začátek generování textury
glBindTexture(GL_TEXTURE_2D, TextureList[Number]); // Nastav texturový List s následujícími parametry
// Parametry 2D textury
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
// Lineárně rozlož (roztáhni) texturu do celého objektu, pokud je textura menší nez plocha objektu
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
// Lineárně rozlož (zmenši) texturu, pokud je textura větší než plocha objektu
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
// Konec generování textury
}
if (TextureImage[0]) // Jestliže existuje obraz textury 0
{
if (TextureImage[0]->data) // Jestliže obraz textury obsahuje nějaká data
{
free(TextureImage[0]->data); // Tak data vymaž z paměti
}
free(TextureImage[0]); // Vymaž celou strukturu obrazu textury
}
Number++; // Přičti počet textur
return Status; // Vrať funkci stav načítání
}
|
Textura se vytváří z obrázku *.bmp, který načítá funkce LoadBMP()
|
|
AUX_RGBImageRec *LoadBMP(char *Filename) // Načti obrázek *.bmp
{
FILE *File=NULL; // Handle souboru
if (!Filename) // Zkus jestli existuje jméno souboru
{
return NULL; // Jinak ukonči funkci
}
File=fopen(Filename,"r"); // Otevři soubor pro čtení
if (File) // Jestliže soubor existuje
{
fclose(File); // Uzavři soubor
return auxDIBImageLoad(Filename); // Načti bitmapu z otevřeného souboru
}
return NULL; // Jestliže se náčítání souboru nezdaří, tak vrať NULL
}
|
Načítání textur volá funkce InitScene() jen jednou při startu aplikace.
|
|
int InitScene(void) // Načti scénu
{
if (!Texture1.LoadGLTextures("Textures/picture01.bmp")) // Zkus načíst texturu
{
return FALSE; // Jestliže se to nepodaří, tak opusť fukci
}
if (!Texture1.LoadGLTextures("Textures/picture02.bmp")) // Zkus načíst texturu
{
return FALSE; // Jestliže se to nepodaří, tak opusť fukci
}
LoadAllObjects(); // Načti objekty ze souboru
InitCube(); // Načti krychli
BuildFont(); // Vytvoř bitmap fonty
return TRUE; // Vrať TRUE pokud vše proběhlo OK
}
|
Pro procházení scénou jsem vytvořil třídu KCamera
|
|
class KCamera // Třída kamer
{
public:
KCamera(); // Konstruktor kamery
// Proměnné
KVector3 Position; // Pozice kamery
KVector3 Angle; // Úhel rotace kamery
KVector3 View; // Pozice bodu, na který kouká kamera
KVector3 Up; // Pozice Up Vectoru
KVector3 Strafe; // Vektor pro posunutí do stran
float speed; // Rychlost kamery
// Funkce
void RotateView(float angle, float X, float Y, float Z); // Rotace bodu, na který kouká kamera
void SetViewByMouse(); // Rotace kamery pomocí myši (First person view)
void StrafeCamera(float speed); // Posun kamery doprava, nebo doleva podle hodnoty speed (+ nebo -)
void MoveCamera(float speed); // Pohyb kamery vpřed a vzad podle hodnoty speed (+ nebo -)
void Look(); // Volá funkci gluLookAt() pro nastavení kamery, podle předem vypočtených hodnot
void GoForward(); // Pohyb dopředu
void GoBackward(); // Pohyb dozadu
void GoUp(); // Pohyb nahoru
void GoDown(); // Pohyb dolu
void StrafeLeft(); // Pohyb doleva
void StrafeRight(); // Pohyb doprava
void TurnLeft(); // Rotace doleva
void TurnRight(); // Rotace doprava
void TurnUp(); // Rotace nahoru
void TurnDown(); // Rotace dolu
};
|
Třída KCamera používá třídu KVector3, která obsahuje
potřebné vektorové algoritmy.
|
|
class KVector3 // Třída vektorů
{
public:
// Základní konstruktor vektorů
KVector3() {}
// Konstruktor, který nám dovoluje kdykoli nastavit vektor
KVector3(float X, float Y, float Z)
{
x = X; y = Y; z = Z;
}
// Přetížený operátor+ aby šel celý vektor sečíst
KVector3 operator+(KVector3 vVector)
{
// Vrať výsledek součtu vektoru
return KVector3(vVector.x + x, vVector.y + y, vVector.z + z);
}
// Přetížený operátor- aby šel celý vektor odečíst
KVector3 operator-(KVector3 vVector)
{
// Vrať výsledek rozdílu vektoru
return KVector3(x - vVector.x, y - vVector.y, z - vVector.z);
}
// Přetížený operátor* aby šel celý vektor násobit
KVector3 operator*(float num)
{
// Vrať výsledek součinu vektoru
return KVector3(x * num, y * num, z * num);
}
// Přetížený operátor/ aby šel celý vektor dělit
KVector3 operator/(float num)
{
// Vrať výsledek dělení vektoru
return KVector3(x / num, y / num, z / num);
}
// Hodnota 3D vektoru
float x, y, z;
};
|
Tyto algoritmy vypadají takto:
|
|
//----------------------------------------------------------------------------------------------------//
// Vypočítej kolmý vektor k vektorům vVector1 a vVector2
KVector3 Cross(KVector3 vVector1, KVector3 vVector2)
{
KVector3 vNormal;
// Vypočítej kolmý vektor (Cross product)
vNormal.x = ((vVector1.y * vVector2.z) - (vVector1.z * vVector2.y));
vNormal.y = ((vVector1.z * vVector2.x) - (vVector1.x * vVector2.z));
vNormal.z = ((vVector1.x * vVector2.y) - (vVector1.y * vVector2.x));
// Vrať výsledek
return vNormal;
}
//----------------------------------------------------------------------------------------------------//
// Vypočítej velikost vektoru
float Magnitude(KVector3 vNormal)
{
// Podle rovnice: magnitude = sqrt(V.x^2 + V.y^2 + V.z^2) :
// V je vektor, sqrt je druhá odmocnina, ^2 znamená na druhou
return (float)sqrt( (vNormal.x * vNormal.x) +
(vNormal.y * vNormal.y) +
(vNormal.z * vNormal.z) );
}
//----------------------------------------------------------------------------------------------------//
// Normalizuj vektor, aby jeho velikost nepřesahovala hodnotu 1, nebo -1
KVector3 Normalize(KVector3 vVector)
{
// Vypočítej velikost vektoru
float magnitude = Magnitude(vVector);
// Vyděl vektor vypočtenou hodnotou velikosti vektoru, aby jsme dostali hodnotu mezi 1, -1
vVector = vVector / magnitude;
// Vrať výsledek
return vVector;
}
//----------------------------------------------------------------------------------------------------//
|
Pohyb kamery provádí OpenGL funkce gluLookAt()
|
|
void KCamera::Look()
{
// Rotuj pomocí myši
SetViewByMouse();
// Pohybuj a rotuj kamerou
gluLookAt(Position.x, Position.y, Position.z, // Pozice bodu kamery
View.x, View.y, View.z, // Pozice bodu, kam kouká kamera
Up.x, Up.y, Up.z); // Pozice Up vektoru Y=1;
}
|
Abychom mohli pohybovat a rotovat kamerou, musíme si vypočítat pozici kamery Position
a pozici bodu, kam kouká kamera View. Up vektor Up měnit nebudeme.
Nejdříve si nastavíme v konstruktoru třídy KCamera základní hodnoty
|
|
KCamera::KCamera() // Konstruktor třídy KCamera
{
// Nastav tyto počáteční hodnoty kamery
Position.x = 0.0; // X pozice kamery
Position.y = 0.0; // Y pozice kamery
Position.z = -10.0; // Z pozice kamery
Angle.x = 0; // X úhel rotace kamery
Angle.y = 0; // Y úhel rotace kamery
Angle.z = 0; // Z úhel rotace kamery
View.x = 0.0; // X pozice bodu, kam kouká kamera
View.y = 0.0; // Y pozice bodu, kam kouká kamera
View.z = -1.0; // Z pozice bodu, kam kouká kamera
Up.x = 0.0; // X pozice Up vektoru
Up.y = 1.0; // Y pozice Up vektoru
Up.z = 0.0; // Z pozice Up vektoru
speed = 0.1f; // Rychlost pohybu kamery
}
|
Funkce RotateView() vypočítá pozici bodu, na který kouká kamera
|
|
// Vypočítej pozici bodu, na který kouká kamera
void KCamera::RotateView(float angle, float x, float y, float z)
{
KVector3 vNewView;
// Vypočítej vektor pohledu kamery
KVector3 vView = View - Position;
// Vytvoř si hodnoty sin, cos, úhlu rotace
float cosTheta = (float)cos(angle);
float sinTheta = (float)sin(angle);
// Najdi novou x pozici pro nový bod, na který kouká kamera
vNewView.x = (cosTheta + (1 - cosTheta) * x * x) * vView.x;
vNewView.x += ((1 - cosTheta) * x * y - z * sinTheta) * vView.y;
vNewView.x += ((1 - cosTheta) * x * z + y * sinTheta) * vView.z;
// Najdi novou y pozici pro nový bod, na který kouká kamera
vNewView.y = ((1 - cosTheta) * x * y + z * sinTheta) * vView.x;
vNewView.y += (cosTheta + (1 - cosTheta) * y * y) * vView.y;
vNewView.y += ((1 - cosTheta) * y * z - x * sinTheta) * vView.z;
// Najdi novou z pozici pro nový bod, na který kouká kamera
vNewView.z = ((1 - cosTheta) * x * z - y * sinTheta) * vView.x;
vNewView.z += ((1 - cosTheta) * y * z + x * sinTheta) * vView.y;
vNewView.z += (cosTheta + (1 - cosTheta) * z * z) * vView.z;
// Přičti k pozici kamery nový rotační vektor, čímž vypočítáš nový bod na který kouká kamera
View = Position + vNewView;
}
|
Funkce MoveCamera() vypočítá pohyb kamery vpřed, nebo vzad, podle hodnoty speed
|
|
// Pohni kamerou vpřed, nebo vzad podle hodnoty speed (+ nebo -)
void KCamera::MoveCamera(float speed)
{
KVector3 vVector = View - Position; // Vypočítej vektor pohledu kamery
vVector = Normalize(vVector); // Normalizuj vektor
Position.x += vVector.x * speed; // Vypočítej novou hodnotu pozice bodu X kamery, podle vektoru pohledu a rychlosti posunutí
Position.y += vVector.y * speed; // Vypočítej novou hodnotu pozice bodu Y kamery, podle vektoru pohledu a rychlosti posunutí
Position.z += vVector.z * speed; // Vypočítej novou hodnotu pozice bodu Z kamery, podle vektoru pohledu a rychlosti posunutí
View.x += vVector.x * speed; // Vypočítej novou hodnotu vektoru pohledu X, podle vektoru pohledu a rychlosti posunutí
View.y += vVector.y * speed; // Vypočítej novou hodnotu vektoru pohledu Y, podle vektoru pohledu a rychlosti posunutí
View.z += vVector.z * speed; // Vypočítej novou hodnotu vektoru pohledu Z, podle vektoru pohledu a rychlosti posunutí
}
|
Funkce StrafeCamera() vypočítá pohyb kamery doleva, nebo doprava, podle hodnoty speed
|
|
// Posuň kameru doprava, nebo doleva podle hodnoty speed (+ nebo -)
void KCamera::StrafeCamera(float speed)
{
// Vypočítej novou hodnotu pozice kamery, podle vektoru posunutí a rychlosti posunutí
Position.x += Strafe.x * speed;
Position.z += Strafe.z * speed;
// Vypočítej novou hodnotu vektoru pohledu, podle vektoru posunutí a rychlosti posunutí
View.x += Strafe.x * speed;
View.z += Strafe.z * speed;
}
|
Další funkce volají předešlé podle toho, jaký pohyb májí vykonat.
|
|
//----------------------------------------------------------------------------------------------------//
void KCamera::GoForward() // Pohni kamerou dopředu
{
MoveCamera(speed);
}
//----------------------------------------------------------------------------------------------------//
void KCamera::GoBackward() // Pohni kamerou dozadu
{
MoveCamera(-speed);
}
//----------------------------------------------------------------------------------------------------//
void KCamera::GoUp() // Pohni kamerou nahoru
{
Position.y += speed; // Pohni pozicí kamery
View.y += speed; // A zároveň pohni bodem, na který kamera kouká
}
//----------------------------------------------------------------------------------------------------//
void KCamera::GoDown() // Pohni kamerou dolu
{
Position.y -= speed; // Pohni pozicí kamery
View.y -= speed; // A zároveň pohni bodem, na který kamera kouká
}
//----------------------------------------------------------------------------------------------------//
void KCamera::StrafeLeft() // Pohni kamerou doleva
{
KVector3 vCross = Cross(View - Position, Up); // Vypočítej kolmý vektor osy na vektor pohledu kamery
Strafe = Normalize(vCross); // Normalizuj vektor posunutí
StrafeCamera(-speed); // Pohni kamerou o hodnotu rychlosti kamery
}
//----------------------------------------------------------------------------------------------------//
void KCamera::StrafeRight() // Pohni kamerou doprava
{
KVector3 vCross = Cross(View - Position, Up); // Vypočítej kolmý vektor osy na vektor pohledu kamery
Strafe = Normalize(vCross); // Normalizuj vektor posunutí
StrafeCamera(speed); // Pohni kamerou o hodnotu rychlosti kamery
}
//----------------------------------------------------------------------------------------------------//
void KCamera::TurnLeft() // Otoč kamerou doleva
{
Angle.y = speed; // Nastav rychlost rotace
RotateView(Angle.y, 0, 1, 0); // Rotuj bodem, na který kouká kamera
}
//----------------------------------------------------------------------------------------------------//
void KCamera::TurnRight() // Otoč kamerou doleva
{
Angle.y = -speed; // Nastav rychlost rotace
RotateView(Angle.y, 0, 1, 0); // Rotuj bodem, na který kouká kamera
}
//----------------------------------------------------------------------------------------------------//
void KCamera::TurnUp() // Otoč kamerou nahoru
{
/*
KVector3 vAxis = Cross(View - Position, Up); // Vypočítej kolmý vektor osy na vektor pohledu kamery
vAxis = Normalize(vAxis); // Normalizuj vektor osy
Angle.z = speed; // Nastav rychlost rotace
RotateView(Angle.z, vAxis.x, vAxis.y, vAxis.z); // Rotuj bodem, na který kouká kamera
*/
}
//----------------------------------------------------------------------------------------------------//
void KCamera::TurnDown() // Otoč kamerou dolu
{
/*
KVector3 vAxis = Cross(View - Position, Up); // Vypočítej kolmý vektor osy na vektor pohledu kamery
vAxis = Normalize(vAxis); // Normalizuj vektor osy
Angle.z = -speed; // Nastav rychlost rotace
RotateView(Angle.z, vAxis.x, vAxis.y, vAxis.z); // Rotuj bodem, na který kouká kamera
*/
}
//----------------------------------------------------------------------------------------------------//
|
Poslední dvě funkce TurnUp() a TurnDown() jsem vypnul, protože kolidují
s rotací kamery pomocí myši, viz. dále. Pokud však myš nebudete chtít použít, tyto dvě
funkce se hodí.
Funkce pohybu kamery se volají tehdy, když zmáčkneme určité klávesy. Aplikace odchytává
zprávy klávesnice WM_KEYDOWN (klávesu jsem stlačil) a WM_KEYUP (klávesu
jsem pustil)
|
|
// Odchytávej zprávy render okna
LRESULT CALLBACK WndProc( HWND hWnd, // Handle render okna
UINT uMsg, // Zprávy render okna
WPARAM wParam, // Dodatečné informační zprávy okna
LPARAM lParam) // Dodatečné informační zprávy okna
{
switch (uMsg) // Testuj zprávy Windows
{
case WM_ACTIVATE: // Hlídej zprávu Window o aktivaci
{
if (!HIWORD(wParam)) // Zkus, jestli není okno minimalizované. Pokud není minimalizovaný
{
active=TRUE; // Tak nastav program jako aktivní
}
else // Pokud je okno minimalizovaný
{
active=FALSE; // Tak nastav program jako neaktivní
}
return 0; // Vrať se a hlídej dál
}
case WM_SYSCOMMAND: // Hlídej, jestli systém nežádá o spuštění spořiče obrazovky, nebo usporného režimu
{
switch (wParam) // Jestliže se tak stane
{
case SC_SCREENSAVE: // Spořič chce startovat ?
case SC_MONITORPOWER: // Usporný režim chce startovat ?
return 0; // Hoď jim nulu ať neotravujou
}
break; // Vrať se a hlídej dál
}
case WM_CLOSE: // Hlídej, jestli není požadavek na ukončení aplikace. Jestli se tak stane
{
PostQuitMessage(0); // Pošli ukončovací zprávu
return 0; // Vrať se
}
case WM_KEYDOWN: // Hlídej, jestli mačkám klávesu. Jestliže mačkám klávesu tak
{
keys[wParam] = TRUE; // Pošli kladnou zprávu TRUE a její parametr. Např klávesa s kódem 40 je keys[40] = TRUE;
return 0; // Vrať se a hlídej dál
}
case WM_KEYUP: // Hlídej, jestli uvolńuju klávesu. Jestliže uvolńuju klávesu
{
keys[wParam] = FALSE; // Pošli zápornou zprávu FALSE a její parametr. Např klávesa s kodem 40 je keys[40] = FALSE;
return 0; // Vrať se a hlídej dál
}
case WM_SIZE: // Hlídej, jestli neměním velikost okna. Pokud ji měním
{
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); // Volej funkci pro změnu OpenGL scény. LoWord=Width, HiWord=Height
return 0; // Vrať se a hlídej dál
}
}
// Další zprávy mě nezajímaj, tak předej funkci DefWindowProc, ať si s nima Windows poradi sám
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
|
V hlavní vstupní funkci WinMain() budeme obsluhovat zprávy klávesnice.
|
|
// Hlavní vstupní funkce aplikace. Zavolej vytvářecí rutiny, obsluhuj zprávy a pohlídej interakci s mými požadavky.
int APIENTRY WinMain(HINSTANCE hInstance, // Instance aplikace
HINSTANCE hPrevInstance, // Předchozí instance
LPSTR lpCmdLine, // Parametry příkazového řádku
int nCmdShow) // Sestavení okna
{
MSG msg; // Struktura Windows zpráv
BOOL done=FALSE; // Proměnna pro ukončeni smyčky. Pokud je done=FALSE aplikace beží, pokud se změní na done=TRUE aplikace se ukončí
// Zeptej se mě, jestli chci používat FullScreen mód, nebo okenní mód
if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE; // Okenní mód
}
// Zkus volat funkci pro vytvořeni OpenGL render okna, s následujícimi parametry. Pokud se to nepodaří tak
if (!CreateGLWindow("Moving Camera",SCREEN_WIDTH,SCREEN_HEIGHT,COLOR_BITS,fullscreen))
{
return 0; // Ukonči aplikaci
}
// Hlavni smyčka aplikace
while(!done) // Smyčka funguje pokud je done=FALSE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Testuj, jestli nepřišla nějaká zpráva ?
{
if (msg.message==WM_QUIT) // Jestliže přišla zpráva o ukončení aplikace tak
{
done=TRUE; // Ukonči aplikaci done=TRUE
}
else // Jinak
{
TranslateMessage(&msg); // Přelož zprávu
DispatchMessage(&msg); // Odešli zprávu a dej ji k dispozici funkci WndProc(), nebo at´ se s ní zabývá Windows
}
}
else // Když nejsou žádné zprávy
{
// Vykresli OpenGL Scénu
if (active) // Je aplikace aktivní ?
{
if (keys[VK_ESCAPE]) // Klepnul jsem na ESC ?
{
done=TRUE; // Tak ukonči aplikaci
}
else // Jestliže jsem neklepnul na klávesu ESC
{
DrawGLScene(); // Volej funkci pro kreslení scény
SwapBuffers(hDC); // Prohoď buffery (Double Buffering)
// Použitím double bufferu kreslíme vše na skrytou obrazovku.
// Když prehazujeme buffer, viditelna obrazovka se stane skrytou
// a skrytá se zobrazí. Touto cestou nevidíme naší scénu blikat.
}
}
if (keys[VK_F1]) // Jestliže jsem klepnul na klávesu F1?
{
keys[VK_F1]=FALSE; // Tak vrať klávese F1 opět hodnotu FALSE
KillGLWindow(); // Ukonči OpenGL instance a zavři okno
fullscreen=!fullscreen; // Přepínej mezi FullScreen a Okennim módem
// Vytvoř nové OpenGL render okno
if (!CreateGLWindow("NeHe's OpenGL Framework",SCREEN_WIDTH,SCREEN_HEIGHT,COLOR_BITS,fullscreen))
{
return 0; // Ukonči aplikaci, pokud se okno nepovedlo vytvořit
}
}
if ((keys[VK_UP]) || (keys['W'])) // Jestliže jsem klepnul na klávesu šipka nahoru, nebo na klávesu W
{
Camera1.GoForward(); // Přepočítej pozici kamery, aby se posunula dopředu
}
if ((keys[VK_DOWN]) || (keys['S'])) // Jestliže jsem klepnul na klávesu šipka dolu, nebo na klávesu S
{
Camera1.GoBackward(); // Přepočítej pozici kamery, aby se posunula dozadu
}
if ((keys[VK_PRIOR]) || (keys['E'])) // Jestliže jsem klepnul na klávesu Page Up, nebo na klávesu E
{
Camera1.GoUp(); // Přepočítej pozici kamery, aby se posouvala nahoru
}
if ((keys[VK_NEXT]) || (keys['C'])) // Jestliže jsem klepnul na klávesu Page Down, nebo na klávesu C
{
Camera1.GoDown(); // Přepočítej pozici kamery, aby se posouvala dolu
}
if (keys['A']) // Jestliže jsem klepnul na klávesu A
{
Camera1.StrafeLeft(); // Přepočítej pozici kamery, aby se posouvala doleva
}
if (keys['D']) // Jestliže jsem klepnul na klávesu D
{
Camera1.StrafeRight(); // Přepočítej pozici kamery, aby se posouvala doprava
}
if (keys[VK_LEFT]) // Jestliže jsem klepnul na klávesu šipka doleva
{
Camera1.TurnLeft(); // Přepočítej rotaci kamery, aby rotovala doleva
}
if (keys[VK_RIGHT]) // Jestliže jsem klepnul na klávesu šipka doprava
{
Camera1.TurnRight(); // Přepočítej rotaci kamery, aby rotovala doprava
}
if (keys[VK_HOME]) // Jestliže jsem klepnul na klávesu Home
{
Camera1.TurnUp(); // Přepočítej rotaci kamery, aby rotovala nahoru
}
if (keys[VK_END]) // Jestliže jsem klepnul na klávesu End
{
Camera1.TurnDown(); // Přepočítej rotaci kamery, aby rotovala dolu
}
if (keys['Q']) // Jestliže jsem klepnul na klávesu Q
{
// Camera1.InclineLeft(); // Přepočítej rotaci kamery, aby se naklonila doleva
}
if (keys['E']) // Jestliže jsem klepnul na klávesu E
{
// Camera1.InclineRight(); // Přepočítej rotaci kamery, aby se naklonila doprava
}
}
}
// Ukončování aplikace
KillGLWindow(); // Ukonči OpenGL instance a zavři okno
return (msg.wParam); // Definitivně ukonči aplikaci
}
|
Funkce pro rotaci kamery pomocí myši SetViewByMouse() je součástí třídy KCamera
|
|
// Rotuj kamerou pomocí myši (First person view)
void KCamera::SetViewByMouse()
{
POINT mousePos; // Pozice kurzoru myši
int middleX = SCREEN_WIDTH >> 1; // X střed obrazovky. Bitový posun doprava, způsobí dělení dvěma šířky obrazovky
int middleY = SCREEN_HEIGHT >> 1; // Y střed obrazovky. Bitový posun doprava, způsobí dělení dvěma výšky obrazovky
float angleY = 0.0f; // Rotace kolem osy Y, což způsobí otočení kamery doprava a doleva
float angleZ = 0.0f; // Směr pohledu kamery nahoru a dolu
static float currentRotX = 0.0f; // Rotace kamery v X směru
// Získej X,Y pozici kurzoru myši
GetCursorPos(&mousePos);
// Pokud je kurzor stále uprostřed okna, nic by se nemělo hýbat, proto opusť funkci
if( (mousePos.x == middleX) && (mousePos.y == middleY) ) return;
// Jinak vrať kurzor zpět do středu okna
SetCursorPos(middleX, middleY);
// Získej úhel Y a Z podle směru pohybu kurzoru
angleY = (float)( (middleX - mousePos.x) ) / 500.0f;
angleZ = (float)( (middleY - mousePos.y) ) / 500.0f;
// Nastav stávájící rotaci kamery v X směru
// Hodnotu pak použíj k tomu, aby kamera v X směru nerotovala 360 stupnu,
// ale aby se zastavovala u stropu a u podlahy
currentRotX -= angleZ;
// Jestliže je stávájící rotace X (která je v radianech) větší než 1, tak vrať hodnotu zpět na 1
if(currentRotX > 1.0f)
currentRotX = 1.0f;
// Jestliže je stávájící rotace X (která je v radianech) menší než -1, tak vrať hodnotu zpět na -1
else if(currentRotX <
-1.0f)
currentRotX = -1.0f;
// Jinak rotuj dále
else
{
// Aby rotovala kamera nahoru a dolu, musíš vypočítat
// kolmý vektor na vektor pohledu kamery.
KVector3 vAxis = Cross(View - Position, Up); // Vypočítej kolmý vektor osy na vektor pohledu kamery
vAxis = Normalize(vAxis); // Normalizuj vektor osy
RotateView(angleZ, vAxis.x, vAxis.y, vAxis.z); // Rotuj nahoru a dolu v nové ose
RotateView(angleY, 0, 1, 0); // Rotuj doleva a doprava
}
}
|
Tato funkce je volána v nekonečné smyčce spolu s funkcí gluLookAt()
|
|
void KCamera::Look()
{
// Rotuj pomocí myši
SetViewByMouse();
// Pohybuj a rotuj kamerou
gluLookAt(Position.x, Position.y, Position.z, // Pozice bodu kamery
View.x, View.y, View.z, // Pozice bodu, kam kouká kamera
Up.x, Up.y, Up.z); // Pozice Up vektoru Y=1;
}
|
Fukcí DrawScene().
|
|
int DrawScene(void) // Vykresli scénu
{
Camera1.Look(); // Pohybuj a rotuj kamerou
DrawCube(); // Vykresli krychli
DrawFps(); // Vykresli FPS
return TRUE; // Vrať TRUE pokud vše proběhlo OK
}
|
A to je vše. Chtěl bych poděkovat NEHE mu, od
kterého jsem převzal základ OpenGL aplikace. Dále bych chtěl poděkovat
DigiBen
ovi, od kterého jsem se naučil pohybovat kamerou. Taky mi pohohl
RAJSOFT junior, od kterého jsem
převzal pár překladů z NEHE tutorialů.
Příště se vrhneme na vytváření okolní krajiny (Sky Box).
Kwan
|
|
Home