//---------------------------------------------------------------------
// Ukazkovy priklad cislo 68
// Autor: Pavel Tisnovsky
//
// Program pro zobrazeni sceny s otexturocanymi telesy. Textury jsou nejdrive
// nacteny z rastrovych souboru typu TGA (Targa) a z techto jsou pote
// zobrazovany ve scene.
// Ovladani bud mysi: otaceni+tlacitka pohyb vpred (leve) a vzad (prave)
// nebo klavesnici:
// sipka nahoru, sipka dolu: pohyb vpred/vzad
// sipka doprava, sipka doleva: otaceni
// CTRL+sipka doprava, doleva: ukroky
//---------------------------------------------------------------------
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <gl/glut.h>
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#define MOVE_SCENE_DOWN -5.0 // posun sceny dolu, aby byla videt rovina x-y
#define SCENE_SHIFT -11.0 // odsunuti sceny od pozorovatele, aby se provadelo korektne otaceni
#define DEFAULT_WINDOW_WIDTH 450 // velikost a pocatecni pozice okna na obrazovce
#define DEFAULT_WINDOW_HEIGHT 450
#define DEFAULT_WINDOW_TOP 10
#define DEFAULT_WINDOW_LEFT 10
#define TEXTURE_GROUND 0 // jmena textur pro vyber
#define TEXTURE_WALL1 1
#define TEXTURE_WALL2 2
#define TEXTURE_TREASURE 3
int textures[4]; // cisla textur
typedef struct Window { // informace o oknu
int width;
int height;
} Window;
typedef struct View { // informace o pohledu na scenu
float fov;
float nearPlane;
float farPlane;
} View;
typedef struct Avatar { // informace o hraci (pozorovateli)
float posX;
float posY;
float angle;
float moveSpeed;
} Avatar;
Window window;
View view={45.0, 2.0, 1000.0};
Avatar avatar={0.0, -80.0, 0.0, 0.0};
int btn=0;
char mapa[10][10]={ // mapa bludiste
{".......###"},
{".#.#### .#"},
{".#.......#"},
{".#...#####"},
{"#*##..#..."},
{"#.#*.....#"},
{"#....#.###"},
{"#.##.#...."},
{".......#.."},
{"########.."},
};
//---------------------------------------------------------------------
// Posun pohledu podle pozice a orientace avatara
//---------------------------------------------------------------------
void avatarMoveView(Avatar *avatar)
{
glTranslatef(0.0, MOVE_SCENE_DOWN, 0.0); // posun sceny dolu, aby byla videt rovina z=0
glRotatef(90.0, 1.0, 0.0, 0.0); // otoceni sceny okolo osy X tak, aby osa Z smerovala nahoru
glTranslatef(0.0, SCENE_SHIFT, 0.0); // posun sceny od pozorovatele, aby se provadelo korektne otaceni
glRotatef(avatar->angle, 0.0, 0.0, 1.0); // otoceni pozorovatele
glTranslatef(avatar->posX, avatar->posY, 0.0); // posun pozorovatele
}
//---------------------------------------------------------------------
// Kontrola, zda se avatar nachazi uvnitr sceny s pripadnym omezenim pohybu
//---------------------------------------------------------------------
void avatarCheckRanges(Avatar *avatar)
{
if (avatar->posX>90.0) avatar->posX=90.0;
if (avatar->posX<-90.0) avatar->posX=-90.0;
if (avatar->posY>90.0) avatar->posY=90.0;
if (avatar->posY<-90.0) avatar->posY=-90.0;
}
//---------------------------------------------------------------------
// Posun avatara ve smeru pohybu
//---------------------------------------------------------------------
void avatarMove(Avatar *avatar)
{
avatar->posX+=avatar->moveSpeed*sin(avatar->angle*3.14/180.0);
avatar->posY+=avatar->moveSpeed*cos(avatar->angle*3.14/180.0);
avatarCheckRanges(avatar);
}
//---------------------------------------------------------------------
// Posun avatara dopredu (podle jeho orientace)
//---------------------------------------------------------------------
void avatarMoveForward(Avatar *avatar)
{
avatar->posX+=1.0*sin(avatar->angle*3.14/180.0);
avatar->posY+=1.0*cos(avatar->angle*3.14/180.0);
avatarCheckRanges(avatar);
}
//---------------------------------------------------------------------
// Posun avatara dozadu (podle jeho orientace)
//---------------------------------------------------------------------
void avatarMoveBackward(Avatar *avatar)
{
avatar->posX-=1.0*sin(avatar->angle*3.14/180.0);
avatar->posY-=1.0*cos(avatar->angle*3.14/180.0);
avatarCheckRanges(avatar);
}
//---------------------------------------------------------------------
// Posun avatara doleva (ukrok podle jeho orientace)
//---------------------------------------------------------------------
void avatarMoveLeft(Avatar *avatar)
{
avatar->posX+=1.0*cos(avatar->angle*3.14/180.0);
avatar->posY-=1.0*sin(avatar->angle*3.14/180.0);
avatarCheckRanges(avatar);
}
//---------------------------------------------------------------------
// Posun avatara doprava (ukrok podle jeho orientace)
//---------------------------------------------------------------------
void avatarMoveRight(Avatar *avatar)
{
avatar->posX-=1.0*cos(avatar->angle*3.14/180.0);
avatar->posY+=1.0*sin(avatar->angle*3.14/180.0);
avatarCheckRanges(avatar);
}
//---------------------------------------------------------------------
// Otoceni avatara smerem doleva
//---------------------------------------------------------------------
void avatarTurnLeft(Avatar *avatar)
{
avatar->angle+=3.0;
}
//---------------------------------------------------------------------
// Otoceni avatara smerem doprava
//---------------------------------------------------------------------
void avatarTurnRight(Avatar *avatar)
{
avatar->angle-=3.0;
}
//---------------------------------------------------------------------
// Nacteni bitmapy ze souboru typu TGA
//---------------------------------------------------------------------
int bitmapLoadFromTGA(int texture, const char *filename)
{
FILE *fin;
unsigned short int width=0, height=0; // sirka a vyska obrazku
unsigned short int palettelength; // delta palety
unsigned char bpp=0; // pocet bitu na pixel
int size;
unsigned char *bitmap;
unsigned char tgaHeader[18]; // hlavicka formatu TGA
if (!filename) return -1;
fin=fopen(filename, "rb");
if (!fin) return -1; // otevreni souboru se nezdarilo
if (fread(tgaHeader, 18, 1, fin)!=1) return -1; // nacist hlavicku BMP souboru
memcpy(&width, tgaHeader+12, 2); // nacist sirku obrazku v pixelech
memcpy(&height, tgaHeader+14, 2); // nacist vysku obrazku v pixelech
memcpy(&bpp, tgaHeader+16, 1); // nacist pocet bitu na pixel
memcpy(&palettelength, tgaHeader+5, 2); // nacist delku palety
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
switch (bpp) {
case 8:
size=width*height;
bitmap=(unsigned char *)malloc(size*sizeof(unsigned char));
if (fread(bitmap, palettelength, 1, fin)!=1) return -1;
if (fread(bitmap, size, 1, fin)!=1) return -1;
fclose(fin);
glTexImage2D(GL_TEXTURE_2D, 0, 1, width, height,// nacteni textury do GPU
0, GL_LUMINANCE, GL_UNSIGNED_BYTE, bitmap);
break;
case 24:
size=width*height*3;
bitmap=(unsigned char *)malloc(size*sizeof(unsigned char));
if (fread(bitmap, size, 1, fin)!=1) return -1;
fclose(fin);
glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height,// nacteni textury do GPU
0, GL_RGB, GL_UNSIGNED_BYTE, bitmap);
break;
case 32:
size=width*height*4;
bitmap=(unsigned char *)malloc(size*sizeof(unsigned char));
if (fread(bitmap, size, 1, fin)!=1) return -1;
fclose(fin);
glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height,// nacteni textury do GPU
0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
break;
default:
break;
}
free(bitmap);
return 0;
}
//---------------------------------------------------------------------
// Nacteni a vytvoreni vsech textur
//---------------------------------------------------------------------
int loadTextures(void)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // zpusob ulozeni bytu v texure
glGenTextures(4, textures); // vytvoreni jmena textur
if (bitmapLoadFromTGA(textures[TEXTURE_GROUND], "ground.tga")) exit(0);
if (bitmapLoadFromTGA(textures[TEXTURE_WALL1], "wall1.tga")) exit(0);
if (bitmapLoadFromTGA(textures[TEXTURE_WALL2], "wall2.tga")) exit(0);
if (bitmapLoadFromTGA(textures[TEXTURE_TREASURE], "treasure.tga")) exit(0);
}
//---------------------------------------------------------------------
// Callback funkce zavolana pri inicializaci aplikace
//---------------------------------------------------------------------
void onInit(void)
{
glClearColor(0.2, 0.2, 0.4, 0.0); // barva pro mazani color-bufferu
glShadeModel(GL_SMOOTH); // nastaveni stinovaciho rezimu
loadTextures(); // nacist vsechny textury
glClearDepth(1.0f); // barva pro mazani z-bufferu
glEnable(GL_DEPTH_TEST); // nastaveni funkce pro testovani hodnot v z-bufferu
glDepthFunc(GL_LESS); // kterou funkci vybrat pro testovani z-bufferu
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);// vylepseni zobrazovani textur
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); // nastaveni vykresleni vyplnenych polygonu
glPointSize(10.0); // nastaveni velikosti vykreslovanych bodu
glEnable(GL_POINT_SMOOTH);
glEnable(GL_LINE_SMOOTH);
glutSetCursor(GLUT_CURSOR_NONE);
}
//---------------------------------------------------------------------
// Callback funkce zavolana pri zmene velikosti okna aplikace
//---------------------------------------------------------------------
void onResize(int width, int height)
{
glViewport(0, 0, width, height); // viditelna oblast
window.width=width;
window.height=height;
}
//---------------------------------------------------------------------
// Callback funkce zavolana pri zmene prekreslovani okna
//---------------------------------------------------------------------
void onDisplay(void)
{
int i, j, k;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// vymazani barvoveho a z-bufferu
// Nastaveni ModelView matice tak, aby se pozorovatel prochazel
// scenou ve stylu dungeonu
glMatrixMode(GL_PROJECTION); // projekcni matice
glLoadIdentity();
gluPerspective(view.fov,(double)window.width/(double)window.height, view.nearPlane, view.farPlane);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
avatarMoveView(&avatar);
// nakresleni texturovane podlahy
glEnable(GL_TEXTURE_2D); // povoleni texturovani
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_GROUND]); // navazani textury
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex3f(-100, -100, 1);
glTexCoord2f(4.0, 0.0); glVertex3f(+100, -100, 1);
glTexCoord2f(4.0, 4.0); glVertex3f(+100, +100, 1);
glTexCoord2f(0.0, 4.0); glVertex3f(-100, +100, 1);
glEnd();
// nakresleni obvodovych zdi
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_WALL1]);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex3f(-100, 100, 1);
glTexCoord2f(9.0, 0.0); glVertex3f( 100, 100, 1);
glTexCoord2f(9.0, 1.0); glVertex3f( 100, 100, -10);
glTexCoord2f(0.0, 1.0); glVertex3f(-100, 100, -10);
glTexCoord2f(0.0, 0.0); glVertex3f(-100, 100, 1);
glTexCoord2f(9.0, 0.0); glVertex3f(-100, -100, 1);
glTexCoord2f(9.0, 1.0); glVertex3f(-100, -100, -10);
glTexCoord2f(0.0, 1.0); glVertex3f(-100, 100, -10);
glTexCoord2f(0.0, 0.0); glVertex3f(-100, -100, 1);
glTexCoord2f(9.0, 0.0); glVertex3f( 100, -100, 1);
glTexCoord2f(9.0, 1.0); glVertex3f( 100, -100, -10);
glTexCoord2f(0.0, 1.0); glVertex3f(-100, -100, -10);
glTexCoord2f(0.0, 0.0); glVertex3f( 100, 100, 1);
glTexCoord2f(9.0, 0.0); glVertex3f( 100, -100, 1);
glTexCoord2f(9.0, 1.0); glVertex3f( 100, -100, -10);
glTexCoord2f(0.0, 1.0); glVertex3f( 100, 100, -10);
glEnd();
// nakresleni vnitrnich zdi a pokladu
for (j=0; j<10; j++) {
for (i=0; i<10; i++) {
if (mapa[i][j]!='.') { // vykreslit zed nebo poklad
if (mapa[i][j]=='#')
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_WALL2]);
else
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_TREASURE]);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex3f( -90+i*20, -90+j*20, 1);
glTexCoord2f(1.0, 0.0); glVertex3f( -80+i*20, -90+j*20, 1);
glTexCoord2f(1.0, 1.0); glVertex3f( -80+i*20, -90+j*20, -10);
glTexCoord2f(0.0, 1.0); glVertex3f( -90+i*20, -90+j*20, -10);
glTexCoord2f(0.0, 0.0); glVertex3f( -90+i*20, -80+j*20, 1);
glTexCoord2f(1.0, 0.0); glVertex3f( -80+i*20, -80+j*20, 1);
glTexCoord2f(1.0, 1.0); glVertex3f( -80+i*20, -80+j*20, -10);
glTexCoord2f(0.0, 1.0); glVertex3f( -90+i*20, -80+j*20, -10);
glTexCoord2f(0.0, 0.0); glVertex3f( -80+i*20, -90+j*20, 1);
glTexCoord2f(1.0, 0.0); glVertex3f( -80+i*20, -80+j*20, 1);
glTexCoord2f(1.0, 1.0); glVertex3f( -80+i*20, -80+j*20, -10);
glTexCoord2f(0.0, 1.0); glVertex3f( -80+i*20, -90+j*20, -10);
glTexCoord2f(0.0, 0.0); glVertex3f( -90+i*20, -90+j*20, 1);
glTexCoord2f(1.0, 0.0); glVertex3f( -90+i*20, -80+j*20, 1);
glTexCoord2f(1.0, 1.0); glVertex3f( -90+i*20, -80+j*20, -10);
glTexCoord2f(0.0, 1.0); glVertex3f( -90+i*20, -90+j*20, -10);
glEnd();
}
}
}
glDisable(GL_TEXTURE_2D);
glFlush(); // provedeni vsech prikazu
glutSwapBuffers(); // a prohozeni bufferu
}
//---------------------------------------------------------------------
// Callback funkce zavolana pri stlaceni ASCII klavesy
//---------------------------------------------------------------------
void onKeyPress(unsigned char key, int x, int y)
{
if (key>='A' && key<='Z') // uprava velkych pismen na mala
key+='a'-'A';
switch (key) { // rozeskok podle stlacene klavesy
case 27: // klavesa Escape
case 'q':
case 'x':
exit(0); // ukonceni programu
break;
case 'f':
glutFullScreen(); // prepnuti na celou obrazovku
break;
case 'w': // prepnuti do okna
glutReshapeWindow(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT);
glutPositionWindow(DEFAULT_WINDOW_LEFT, DEFAULT_WINDOW_TOP);
break;
default:
break;
}
glutPostRedisplay();
}
//---------------------------------------------------------------------
// Callback funkce zavolana pri stlaceni non-ASCII klavesy
//---------------------------------------------------------------------
void onKeyDown(int key, int x, int y)
{
int modifiers=glutGetModifiers(); // ziskat stav klaves ALT, CTRL a SHIFT
switch (key) {
case GLUT_KEY_UP:
avatarMoveForward(&avatar);
break;
case GLUT_KEY_DOWN:
avatarMoveBackward(&avatar);
break;
case GLUT_KEY_LEFT:
if (modifiers & GLUT_ACTIVE_CTRL) // CTRL+sipka je ukrok
avatarMoveLeft(&avatar);
else
avatarTurnLeft(&avatar);
break;
case GLUT_KEY_RIGHT:
if (modifiers & GLUT_ACTIVE_CTRL) // CTRL+sipka je ukrok
avatarMoveRight(&avatar);
else
avatarTurnRight(&avatar);
break;
default: break;
}
glutPostRedisplay();
}
//---------------------------------------------------------------------
// Callback funkce zavolana pri stlaceni nebo pusteni tlacitka mysi
//---------------------------------------------------------------------
void onMouseButton(int button, int state, int x, int y)
{
if (button==GLUT_LEFT_BUTTON) {
if (state==GLUT_DOWN) {
btn=1; // zrychleni dopredu
}
else {
btn=0;
}
}
if (button==GLUT_RIGHT_BUTTON) {
if (state==GLUT_DOWN) {
btn=2; // zrychleni dozadu
}
else {
btn=0;
}
}
}
//---------------------------------------------------------------------
// Callback funkce zavolana pri pohybu mysi
//---------------------------------------------------------------------
void onMousePassiveMotion(int x, int y)
{
static int first=1;
static int old_x;
if (first) {
old_x=x;
first=0;
}
else {
avatar.angle=-x+old_x;
}
glutPostRedisplay();
}
//---------------------------------------------------------------------
// Callback funkce zavolana pri tiku casovace kazdych 10ms
//---------------------------------------------------------------------
void onTimer(int timer)
{
if (btn==1) avatar.moveSpeed=5.0; // leve tlacitko mysi -> plna rychlost dopredu
if (btn==2) avatar.moveSpeed=-5.0; // prave tlacitko mysi -> plna rychlost dozadu
if (btn==0) { // zadne tlacitko mysi -> zpomaleni
if (avatar.moveSpeed>0.1) avatar.moveSpeed-=0.5;
if (avatar.moveSpeed<-0.1) avatar.moveSpeed+=0.5;
}
if (abs(avatar.moveSpeed)>0.01) {
avatarMove(&avatar);
glutPostRedisplay();
}
else {
avatar.moveSpeed=0.0;
}
glutTimerFunc(10, onTimer, timer);
}
//---------------------------------------------------------------------
// Callback funkce zavolana pri tiku casovace kazdych 10ms
//---------------------------------------------------------------------
int main(int argc, char **argv)
{
glutInit(&argc,argv); // inicializace knihovny GLUT
glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE|GLUT_DEPTH); // graficky mod okna
glutInitWindowSize(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT);// pocatecni velikost okna
glutInitWindowPosition(DEFAULT_WINDOW_LEFT, DEFAULT_WINDOW_TOP); // pocatecni pozice okna
glutCreateWindow("Priklad cislo 68"); // vytvoreni okna pro kresleni
glutDisplayFunc(onDisplay); // registrace funkce volane pri prekreslovani
glutReshapeFunc(onResize); // registrace funkce volane pri zmene velikosti
glutKeyboardFunc(onKeyPress); // registrace funkce volane pri stisku ASCII klavesy
glutSpecialFunc(onKeyDown); // registrace funkce volane pri stisku non-ASCII klavesy
glutMouseFunc(onMouseButton); // registrace funkce volane pri stisku ci pusteni tlacitka mysi
glutPassiveMotionFunc(onMousePassiveMotion); // registrace funkce volane pri pohybu mysi
glutTimerFunc(10, onTimer, 0x1234); // registrace funkce volane pri tiku casovace
onInit(); // inicializace aplikace
glutMainLoop(); // nekonecna smycka, kde se volaji zaregistrovane funkce
return 0; // ANSI C potrebuje ukoncit fci main prikazem return
// i kdyz se sem program nikdy nedostane
}
//---------------------------------------------------------------------
// finito
//---------------------------------------------------------------------