W tym odcinku kursu poznamy mechanizmy biblioteki OpenGL umożliwiające dowolne przekształcenia geometryczne obiektów. Wykorzystamy także stos macierzy w celu modelowania sceny 3D zawierającej wiele elementów.
Obrót
Obrót w OpenGL wykonują funkcje glRotated i glRotatef:
void glRotated( GLdouble angle, GLdouble x, GLdouble y, GLdouble z )
void glRotatef( GLfloat angle, GLfloat x, GLfloat y, GLfloat z )
których parametry oznaczają:
Obrót realizowany jest w kierunku przeciwnym do ruchu wskazówek zegara w kierunku prostej wyznaczonej przez wektor [x, y, z] zaczepionym w początku układu współrzędnych. Jeżeli wektor ma długość rożną od 1, wektor jest normalizowany.
Funkcje glRotated i glRotatef mnożną bieżącą macierz przez przez macierz obrotu, która ma następującą postać (parametr angle zamieniono na a):
Skalowanie
Skalowanie wykonują następujące funkcje:
void glScalef( GLfloat x, GLfloat y, GLfloat z )
void glScalex( GLfixed x, GLfixed y, GLfixed z )
gdzie x, y, z sa współczynnikami skalowania względem kolejnych osi układu współrzędnych. Funkcje te mnożą bieżącą macierz przez macierz skalowania, która ma postać:
Przesunięcie
Trzecia z podstawowych operacji geometrycznych jest przesuniecie o wektor (transalcja), która realizuja funkcje:
void glTranslatef( GLfloat x, GLfloat y, GLfloat z )
void glTranslatex( GLfixed x, GLfixed y, GLfixed z )
gdzie x, y, z sa współrzędnymi wektora przesunięcia. Podobnie jak poprzednio opisane funkcje, glTranslatef i glTranslatex mnożą bieżącą macierz przez macierz translacji, która ma postać:
Mnożenie macierzy
W przypadku, gdy zestaw standardowych przekształceń macierzowych okazuje się niewystarczający, biblioteka OpenGL udostępnią możliwość przemnożenia bieżącej macierzy przez macierz określoną funkcja z grupy glMultMatrix:
void glMultMatrixd( const GLdouble * m )
void glMultMatrixf( const GLfloat * m )
Parametr m obu funkcji zawiera wskaznik na dane macierzy o rozmiarach 4 × 4, które musza byc ułozone kolejno kolumnami, tj. w kolejności transponowanej w stosunku do przyjetej w jezykach C i C++:
Ładowanie macierzy
Ostatnia z opisywanych operacji na macierzach jest zastepowanie bieżącej macierzy przez dowolnie okreslona macierz o rozmiarach 4 × 4. Operacje te wykonuja funkcje z grupy glLoadMatrix:
void glLoadMatrixd( const GLdouble * m )
void glLoadMatrixf( const GLfloat * m )
Format danych ładowanej macierzy jest taki sam jak w przedstawionych wyzej funkcjach glMultMatrixd i glMultMatrixf.
Składanie przekształceń
Macierzowy opis przekształceń geometrycznych znacznie ułatwia składanie kilku rożnych przekształcen obiektów. Wywołując funkcje generujące macierze odpowiednich przekształceń dokonujemy jednocześnie ich składania. W tej sytuacji jedyna rzeczą, o której należy pamiętać, to kolejność przekształceń, bowiem od tego zależy końcowy efekt. Przykładowy program (plik przeksztalcenia.cpp) przedstawia przekształcenia geometryczne dostępne w bibliotece OpenGL. Scena 3D zawiera pojedynczy obiekt geometryczny zawarty w bibliotece GLUT. Klawisze kursora pozwalają na obracanie wyświetlanego obiektu, przyciski ?+? i ?-? na jego skalowanie, a lewy przycisk myszki przesuwa obiekt. W ramach eksperymentu Czytelnik może w programie zamienić kolejność składania wykonywanych tam przekształceń (funkcja Display) np. przesunięcia i obrotu. Poznajmy jeszcze nowe elementy biblioteki GLUT wykorzystanie w przykładowym programie tj. sposób obsługi myszki i zdefiniowane obiekty geometryczne. Biblioteka GLUT udostępnia obiekty 3D w wersji szkieletowej (tzw. ?druciaki?) oraz w wersji pełnej. W najbliższych przykładowych programach wykorzystamy wersje szkieletowe obiektów.
Kula
Powierzchnia kuli rysowana jest jako szkielet składajacy sie z południków i równolezników (rysunek 1). Powierzchnie kuli, której środek znajduje się w poczatku układu współrzednych rysuje funkcja:
void glutWireSphere( GLdouble radius, GLint slices, GLint stacks )
której parametry oznaczają:
Sześcian
Szescian o boku długosci size i srodku połozonym w poczatku układu współrzednych rysuje funkcja:
void glutWireCube( GLdouble size )
Stożek
Stozek rysowany jest podobnie jak kula - jako szkielet oparty na równoległe do podstawy ?południki? i tworzace biegnace od wierzchołka stozka do krawedzi jego podstawy (rysunek 2). Stozek ze srodkiem podstawy umieszczonym w poczatku układu współrzednych i wierzchołkiem umieszczonym na dodatniej półosi OZ rysuje funkcja:
void glutWireCone( GLdouble base, GLdouble height, GLint slices, GLint stacks )
której parametry oznaczają:
Torus
Kolejna bryła obrotowa dostepna w bibliotece GLUT jest torus rysowany jako seria walców o nierównoległych podstawach (rysunek 3) i osi obrotu pokrywajacej sie z osia OZ. Torus rysuje funkcja:
void glutWireTorus( GLdouble innerRadius, GLdouble outerRadius, GLint sides, GLint rings )
której parametry oznaczaja odpowiednio:
Dwunastoscian
Dwunastoscian o srodku połozonym w poczatku układu współrzędnych rysuje funkcja:
void glutWireDodecahedron( void )
Czajnik
Klasycznym obiektem 3D dostepnym w bibliotece GLUT jest opracowany w 1975 roku przez Martina Newella czajnik - przedstawiony na rysunku 4. Czajnik o wielkosci regulowanej parametrem size rysuje funkcja:
void glutWireTeapot( GLdouble size )
Ośmiościan
Osmioscian o srodku połozonym w poczatku układu współrzednych rysuje funkcja:
void glutWireOctahedron( void )
Czworościan
Czworoscian o srodku połozonym w poczatku układu współrzednych rysuje funkcja:
void glutWireTetrahedron( void )
Dwudziestościan
Dwudziestoscian o srodku połozonym w poczatku układu współrzędnych rysuje funkcja:
void glutWireIcosahedron( void )
Obsługa myszki
Obsługa myszki składa sie z dwóch etapów i jest wykonywana przez dwie funkcje. Pierwsza z nich to:
void MouseButton( int button, int state, int x, int y )
która wykrywa nacisniecie i zwolnienie lewego przycisku myszki. W zależności od stanu lewego przycisku myszki ustawiana jest wartosc zmiennej globalnej button state na przekazana przez parametr state. Druga funkcja:
void MouseMotion( int x, int y )
wywoływana jest podczas ruchu kursora myszki a jej zadaniem jest odpowiednie (dobrane czesciowo doswiadczalnie) przeliczenie ruchu myszki na przesuniecie obiektu znajdujacego sie na scenie. Efekt przeliczen ruchu kursora myszki umieszczony jest w zmiennych globalnych translatex i translatey, które sa bezposredni przekazywane jako parametry funkcji glTranslatef. Parametry x i y obu funkcji oznaczaja współrzedne kursora myszki w odniesieniu do układu współrzednych okna, które oczywiscie nie maja nic wspólnego ze współrzednymi okreslonymi w scenie 3D. Parametr buton funkcji MouseButton okresla który przycisk myszki został nacisniety lub zwolniony. Parametr ten przyjmuje jedna z wartosci:
Jezeli w danym systemie myszka ma tylko dwa przyciski, wartosc GLUT MIDDLE BUTTON nie bedzie generowana. Natomiast w przypadku myszki z jednym przyciskiem funkcja generuje jedynie wartosc GLUT LEFT BUTTON. Ostatni nieopisany parametr funkcji MouseButton to state, który okresla czy przycisk myszki został nacisniety (stała GLUT UP) czy zwolniony (stała GLUT - DOWN).
Aby opisane funkcje obsługi myszki działały nalezy je dołaczyc do listy funkcji zwrotnych wywołujac odpowiednio:
void glutMouseFunc( void( * func )( int button, int state, int x, int y ) )
void glutMotionFunc( void( * func )( int x, int y ) )
Menu wielopoziomowe
W programie został uzyty nowy element biblioteki GLUT ? wielopoziomowe menu podreczne. Czesc menu podrecznego, które bedzie znajdowało sie na drugim (lub wyzszym) poziomie tworzymy w taki sam sposób jak dotychczas tworzylismy główne menu, czyli przy uzyciu funkcji glutCreateMenu i glutAddMenuEntry. Tak utworzony fragment menu umieszczamy w menu głównym (nadrzednym) uzywajac funkcji:
void glutAddSubMenu( const char * label, int submenu )
Parametr label okresla nazwe menu podrecznego, a submenu jego identyfikator zwrócony przez funkcje glutCreateMenu.
Odrysowanie okna
Aby zmiany sceny 3D zostały wyswietlone na ekranie potrzebne jest odrysowanie biezacego okna. Mozna to zrealizowac wywołujac funkcje:
void glutPostRedisplay( void )
Plik przeksztalcenia.cpp
#include <GL/glut.h>
#include <stdlib.h>
enum
{
FULL_WINDOW,
ASPECT_1_1,
WIRE_SPHERE,
WIRE_CONE,
WIRE_CUBE,
WIRE_TORUS,
WIRE_DODECAHEDRON,
WIRE_TEAPOT,
WIRE_OCTAHEDRON,
WIRE_TETRAHEDRON,
WIRE_ICOSAHEDRON,
EXIT
};
int aspect = FULL_WINDOW;
int object = WIRE_SPHERE;
const GLdouble left = - 10.0;
const GLdouble right = 10.0;
const GLdouble bottom = - 10.0;
const GLdouble top = 10.0;
const GLdouble near = 50.0;
const GLdouble far = 70.0;
GLfloat scale = 1.0;
GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;
GLfloat translatex = 0.0;
GLfloat translatey = 0.0;
int button_state = GLUT_UP;
int button_x, button_y;
void Display()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -( near + far ) / 2 );
glTranslatef( translatex, translatey, 0.0 );
glScalef( scale, scale, scale );
glRotatef( rotatex, 1.0, 0, 0 );
glRotatef( rotatey, 0, 1.0, 0 );
glColor3f( 0.0, 0.0, 0.0 );
switch( object )
{
case WIRE_SPHERE:
glutWireSphere( 1.0, 20, 10 );
break;
case WIRE_CONE:
glutWireCone( 1.0, 2.0, 20, 10 );
break;
case WIRE_CUBE:
glutWireCube( 1.0 );
break;
case WIRE_TORUS:
glutWireTorus( 0.2, 1, 10, 20 );
break;
case WIRE_DODECAHEDRON:
glutWireDodecahedron();
break;
case WIRE_TEAPOT:
glutWireTeapot( 1.0 );
break;
case WIRE_OCTAHEDRON:
glutWireOctahedron();
break;
case WIRE_TETRAHEDRON:
glutWireTetrahedron();
break;
case WIRE_ICOSAHEDRON:
glutWireIcosahedron();
break;
}
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
if( aspect == ASPECT_1_1 )
{
if( width < height && width > 0 )
glFrustum( left, right, bottom * height / width, top * height / width, near, far );
else
if( width >= height && height > 0 )
glFrustum( left * width / height, right * width / height, bottom, top, near, far );
}
else
glFrustum( left, right, bottom, top, near, far );
Display();
}
void Keyboard( unsigned char key, int x, int y )
{
if( key == '+' )
scale += 0.1;
else
if( key == '-' && scale > 0.1 )
scale -= 0.1;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}
void SpecialKeys( int key, int x, int y )
{
switch( key )
{
case GLUT_KEY_LEFT:
rotatey -= 1;
break;
case GLUT_KEY_UP:
rotatex -= 1;
break;
case GLUT_KEY_RIGHT:
rotatey += 1;
break;
case GLUT_KEY_DOWN:
rotatex += 1;
break;
}
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}
void MouseButton( int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON )
{
button_state = state;
if( state == GLUT_DOWN )
{
button_x = x;
button_y = y;
}
}
}
void MouseMotion( int x, int y )
{
if( button_state == GLUT_DOWN )
{
translatex += 1.1 *( right - left ) / glutGet( GLUT_WINDOW_WIDTH ) *( x - button_x );
button_x = x;
translatey += 1.1 *( top - bottom ) / glutGet( GLUT_WINDOW_HEIGHT ) *( button_y - y );
button_y = y;
glutPostRedisplay();
}
}
void Menu( int value )
{
switch( value )
{
case FULL_WINDOW:
aspect = FULL_WINDOW;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case ASPECT_1_1:
aspect = ASPECT_1_1;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case WIRE_SPHERE:
object = WIRE_SPHERE;
Display();
break;
case WIRE_CONE:
object = WIRE_CONE;
Display();
break;
case WIRE_CUBE:
object = WIRE_CUBE;
Display();
break;
case WIRE_TORUS:
object = WIRE_TORUS;
Display();
break;
case WIRE_DODECAHEDRON:
object = WIRE_DODECAHEDRON;
Display();
break;
case WIRE_TEAPOT:
object = WIRE_TEAPOT;
Display();
break;
case WIRE_OCTAHEDRON:
object = WIRE_OCTAHEDRON;
Display();
break;
case WIRE_TETRAHEDRON:
object = WIRE_TETRAHEDRON;
Display();
break;
case WIRE_ICOSAHEDRON:
object = WIRE_ICOSAHEDRON;
Display();
break;
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
glutInitWindowSize( 400, 400 );
#ifdef WIN32
glutCreateWindow( "Przekształcenia" );
#else
glutCreateWindow( "Przeksztalcenia" );
#endif
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
glutKeyboardFunc( Keyboard );
glutSpecialFunc( SpecialKeys );
glutMouseFunc( MouseButton );
glutMotionFunc( MouseMotion );
int MenuAspect = glutCreateMenu( Menu );
#ifdef WIN32
glutAddMenuEntry( "Aspekt obrazu - całe okno", FULL_WINDOW );
#else
glutAddMenuEntry( "Aspekt obrazu - cale okno", FULL_WINDOW );
#endif
glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
int MenuObject = glutCreateMenu( Menu );
glutAddMenuEntry( "Kula", WIRE_SPHERE );
#ifdef WIN32
glutAddMenuEntry( "Stożek", WIRE_CONE );
glutAddMenuEntry( "Sześcian", WIRE_CUBE );
glutAddMenuEntry( "Torus", WIRE_TORUS );
glutAddMenuEntry( "Dwunastościan", WIRE_DODECAHEDRON );
glutAddMenuEntry( "Czajnik", WIRE_TEAPOT );
glutAddMenuEntry( "Ośmiościan", WIRE_OCTAHEDRON );
glutAddMenuEntry( "Czworościan", WIRE_TETRAHEDRON );
glutAddMenuEntry( "Dwudziestościan", WIRE_ICOSAHEDRON );
#else
glutAddMenuEntry( "Stozek", WIRE_CONE );
glutAddMenuEntry( "Szescian", WIRE_CUBE );
glutAddMenuEntry( "Torus", WIRE_TORUS );
glutAddMenuEntry( "Dwunastoscian", WIRE_DODECAHEDRON );
glutAddMenuEntry( "Czajnik", WIRE_TEAPOT );
glutAddMenuEntry( "Osmioscian", WIRE_OCTAHEDRON );
glutAddMenuEntry( "Czworoscian", WIRE_TETRAHEDRON );
glutAddMenuEntry( "Dwudziestoscian", WIRE_ICOSAHEDRON );
#endif
glutCreateMenu( Menu );
glutAddSubMenu( "Aspekt obrazu", MenuAspect );
glutAddSubMenu( "Obiekt", MenuObject );
#ifdef WIN32
glutAddMenuEntry( "Wyjście", EXIT );
#else
glutAddMenuEntry( "Wyjscie", EXIT );
#endif
glutAttachMenu( GLUT_RIGHT_BUTTON );
glutMainLoop();
return 0;
}
Stos macierzy modelowania
Do składania przekształcen biblioteka OpenGL wykorzystuje wspomniany juz wczesniej stos macierzy modelowania. W dotychczasowych programach, oraz pierwszym z przykładowych programów z tego odcinka kursu, mechanizm stosu nie był faktycznie wykorzystywany. Wszystkie operacje zwiazane ze stosem macierzy modelowania wykonywane były na macierzy połozonej na szczycie stosu. Pozwala to jednak na efektywna manipulacje jedynie jednym obiektem na scenie. Oczywiscie obsługa wielu obiektów także jest mozliwa, ale wymaga to stosowania mniej wygodnych i efektywnych mechanizmów. W szczególnosci mogło by sie okazac konieczne przechowywanie macierzy modelowania - czyli dublowanie mozliwosci oferowanej przez mechanizm stosu.
Po lekturze powyzszego wstepu Czytelnik zastanawia sie zapewne jak w praktyce efektywnie mozna wykorzystac mechanizm stosu macierzy modelowania. Klasycznym przykładem jest budowa obiektów złozonych z wielu obiektów podstawowych (np. takich jak wyzej opisane obiekty 3D dostępne w bibliotece GLUT). Drugim, równie czesto wykorzystywanym przykładem, jest animacja sceny składajacej sie z wielu obiektów 3D.
Operacje na stosie
Specyfikacja biblioteki OpenGL przewiduje, ze stos macierzy modelowania przechowuje co najmniej 32 macierze. Oczywiscie konkretne implementacje OpenGL moga dowolnie zwiekszac maksymalna pojemnosc tego stosu. Opisane ponizej funkcje słuza do operacji na kazdym rodzaju stosu dostepnym w OpenGL. Operacja zawsze zostanie wykonana na aktualnie wybranym rodzaju stosu. Operacje odłozenia biezacej macierzy na stos realizuje funkcja:
void glPushMatrix( void )
Natomiast operacje zdjecia macierzy ze stosu wykonuje funkcja:
Próba wykonania niedozwolonej operacji na stosie spowoduje wygenerowanie kodu błedu zwracanego przez funkcje glGetError:
Przykładowy program (plik stos modelowania.cpp) opiera sie na poprzednim przykładzie. Podstawowa róznica jest rysowanie jednego obiektu - piramidy, ale złozonego z duzej ilosci elementów podstawowych - szescianów. Dla ułatwienia pracy program zawiera dwie funkcje rysujace czesci piramidy w postaci bloków 3 × 3 (funkcja Cube3x3) i bloków 2 × 2 (funkcja Cube2x2). Pierwszy poziom piramidy ma rozmiary 6 × 6 i jest rysowany z czterech bloków 3 × 3 (funkcja Pyramid). Przed narysowaniem kazdego z elementów pierwszego poziomu macierz modelowania odkładana jest na stos ? po zakonczeniu rysowania nastepuje zdjecie macierzy ze stosu. Przy rysowaniu drugiego i trzeciego poziomu piramidy nastepuje dwukrotne odłozenie macierzy modelowania na stos. Po pierwszym przesuwamy układ współrzednych od odpowiednia ilosc jednostek do góry. Drugie w kolejności odkładanie macierzy na stos wiaze sie juz bezposrednio z rysowaniem kolejnych elementów danego poziomu piramidy. Rysowanie kolejnych poziomów nie wymaga juz dodatkowego opisu ? wystarcza jednokrotne odłozenie macierzy modelowania na stos. Przykładowy efekt tych wszystkich przekształcen zawiera rysunek 5. Oczywiscie uzyskanie takiej figury nie wymaga stosowania mechanizmu stosu macierzy modelowania. Jednak juz w tak prostym przykładzie widoczne sa korzysci wynikajace z takiego rozwiazania. Po pierwsze wszystkie elementy obiektu 3D, w tym wypadku poziomy piramidy, sa od siebie niezalezne. Czytelnik moze to łatwo sprawdzic odpowiednio modyfikujac tekst zródłowy programu. Trudno nie docenic takiej cechy przy tworzeniu bardziej skomplikowanych obiektów. Druga zaleta jest duza przejrzystosc rozwiazania, co ułatwia pózniejsza modyfikacje programu i usuwanie ewentualnych błedów.Warto takze dodac, ze mechanizm stosu jest bardzo szybki i dotyczy to wszystkich implementacji biblioteki OpenGL, nie tylko sprzetowych.
W programie wykorzystano takze kilka innych nieopisanych wczesniej funkcji biblioteki OpenGL i GLU. W poprzednim przykładzie przy przesuwaniu obiektu za pomoca myszki obliczanie wektora przesuniecia oparto o dobrane doswiadczalnie współczynniki. W przypadku, gdy zmienimy parametry bryły odcinania te współczynniki trzeba bedzie dobierac ponownie. Biblioteka GLU zawiera funkcje przeliczajace współrzedne w przestrzeni okna na współrzedne w przestrzeni widoku i odwrotnie.
Przeliczenie współrzednych w przestrzeni widoku (objx, objy, objz) na współrzedne w przestrzeni okna (winx,winy,winz) wykonuje funkcja gluProject:
GLint gluProject( GLdouble objx, GLdouble objy, GLdouble objz,
const GLdouble modelMatrix[ 16 ], const GLdouble projMatrix[ 16 ], const GLint viewport[ 4 ],
GLdouble * winx, GLdouble * winy, GLdouble * winz )
Do przeliczania współrzednych funkcja gluProject wykorzystuje macierz modelowania, macierz rzutowania oraz współrzedne okna renderingu, które zawarte sa kolejno w parametrach: modelMatrix, projMatrix i viewport
Przeliczenie współrzednych w przestrzeni okna (winx,winy,winz) na współrzedne w przestrzeni widoku (objx, objy, objz) wykonuje funkcja gluUnProject:
GLint gluUnProject( GLdouble winx, GLdouble winy, GLdouble winz,
const GLdouble modelMatrix[ 16 ], const GLdouble projMatrix[ 16 ], const GLint viewport[ 4 ],
GLdouble * objx, GLdouble * objy, GLdouble * objz )
której parametry maja analogiczne znaczenie jak parametry poprzedniej funkcji gluProject.
W wersji 1.3 biblioteki GLU dodano funkcje gluUnProject4:
GLint gluUnProject4( GLdouble winx, GLdouble winy, GLdouble winz, GLdouble clipw,
const GLdouble modelMatrix[ 16 ], const GLdouble projMatrix[ 16 ], const GLint viewport[ 4 ],
GLdouble near, GLdouble far, GLdouble * objx,
GLdouble * objy, GLdouble * objz, GLdouble * objw )
stanowiaca rozszerzenie gluUnProject umozliwiajaca obliczenia przy niestandardowych ustawieniach bufora głebokosci (parametry near i far odpowiadające parametrom funkcji glDepthRange) lub w przypadku, gdy czwarta współrzedna (parametr clipw) przestrzeni okna przyjmuje inna wartość niz 1. Funkcja zwraca dodatkowo wartosc czwartej współrzednej w przestrzeni widoku (parametr objw). Znaczenie tych dodatkowych współrzędnych poznamy w kolejnych odcinkach kursu. Wartosci macierzy modelowania, macierzy rzutowania oraz obszaru renderingu stanowia zmienne stanu maszyny stanu OpenGL. Do odczytu zmiennych stanu słuzy bardzo liczna grupa funkcji glGet, z których najbardziej uniwersalne sa nastepujace funkcje:
void glGetBooleanv( GLenum pname, GLboolean * params )
void glGetDoublev( GLenum pname, GLdouble * params )
void glGetFloatv( GLenum pname, GLfloat * params )
void glGetIntegerv( GLenum pname, GLint * params )
Parametr pname okresla która wartosc maszyny stanów OpenGL chcemy pobrac (tabela wszystkich mozliwych wartosci zajmuje 35 stron specyfikacji OpenGL 2.1), a params wskaznik na zwracana wartosc. W zależności od rodzaju pobieranej wartosci params wskazuje na pojedyncza zmienna lub tablice. Rodzaj zwracanej (zwracanych) wartosci jednoznacznie okresla koncowa czesc nazwy funkcji.
Plik stos modelowania.cpp
#include <GL/glut.h>
#include <stdlib.h>
enum
{
FULL_WINDOW,
ASPECT_1_1,
EXIT
};
int aspect = FULL_WINDOW;
const GLdouble left = - 10.0;
const GLdouble right = 10.0;
const GLdouble bottom = - 10.0;
const GLdouble top = 10.0;
const GLdouble near = 50.0;
const GLdouble far = 70.0;
GLfloat scale = 1.0;
GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;
GLfloat translatex = 0.0;
GLfloat translatey = 0.0;
int button_state = GLUT_UP;
int button_x, button_y;
void Cube3x3()
{
glutWireCube( 1.0 );
glTranslatef( 1.0, 0.0, 0.0 );
glutWireCube( 1.0 );
glTranslatef( 0.0, - 1.0, 0.0 );
glutWireCube( 1.0 );
glTranslatef( - 1.0, 0.0, 0.0 );
glutWireCube( 1.0 );
glTranslatef( - 1.0, 0.0, 0.0 );
glutWireCube( 1.0 );
glTranslatef( 0.0, 1.0, 0.0 );
glutWireCube( 1.0 );
glTranslatef( 0.0, 1.0, 0.0 );
glutWireCube( 1.0 );
glTranslatef( 1.0, 0.0, 0.0 );
glutWireCube( 1.0 );
glTranslatef( 1.0, 0.0, 0.0 );
glutWireCube( 1.0 );
}
void Cube2x2()
{
glutWireCube( 1.0 );
glTranslatef( 1.0, 0.0, 0.0 );
glutWireCube( 1.0 );
glTranslatef( 0.0, - 1.0, 0.0 );
glutWireCube( 1.0 );
glTranslatef( - 1.0, 0.0, 0.0 );
glutWireCube( 1.0 );
}
void Pyramid()
{
glPushMatrix();
glTranslatef( 1.5, 1.5, 0.0 );
Cube3x3();
glPopMatrix();
glPushMatrix();
glTranslatef( 1.5, - 1.5, 0.0 );
Cube3x3();
glPopMatrix();
glPushMatrix();
glTranslatef( - 1.5, - 1.5, 0.0 );
Cube3x3();
glPopMatrix();
glPushMatrix();
glTranslatef( - 1.5, 1.5, 0.0 );
Cube3x3();
glPopMatrix();
glPushMatrix();
glTranslatef( 0.0, 0.0, 1.0 );
glPushMatrix();
glTranslatef( 1.0, 1.0, 0.0 );
Cube3x3();
glPopMatrix();
glPushMatrix();
glTranslatef( 1.0, - 1.0, 0.0 );
Cube2x2();
glPopMatrix();
glPushMatrix();
glTranslatef( - 2.0, 2.0, 0.0 );
Cube2x2();
glPopMatrix();
glPushMatrix();
glTranslatef( - 1.0, - 1.0, 0.0 );
Cube3x3();
glPopMatrix();
glPopMatrix();
glPushMatrix();
glTranslatef( 0.0, 0.0, 2.0 );
glPushMatrix();
glTranslatef( 0.5, - 0.5, 0.0 );
Cube2x2();
glPopMatrix();
glPushMatrix();
glTranslatef( 0.5, 1.5, 0.0 );
Cube2x2();
glPopMatrix();
glPushMatrix();
glTranslatef( - 1.5, 1.5, 0.0 );
Cube2x2();
glPopMatrix();
glPushMatrix();
glTranslatef( - 1.5, - 0.5, 0.0 );
Cube2x2();
glPopMatrix();
glPopMatrix();
glPushMatrix();
glTranslatef( 0.0, 0.0, 3.0 );
Cube3x3();
glPopMatrix();
glPushMatrix();
glTranslatef( 0.0, 0.0, 4.0 );
glTranslatef( - 0.5, 0.5, 0.0 );
Cube2x2();
glPopMatrix();
glPushMatrix();
glTranslatef( 0.0, 0.0, 5.0 );
glutWireCube( 1.0 );
glPopMatrix();
}
void Display()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -( near + far ) / 2 );
glTranslatef( translatex, translatey, 0.0 );
glScalef( scale, scale, scale );
glRotatef( rotatex, 1.0, 0, 0 );
glRotatef( rotatey, 0, 1.0, 0 );
glColor3f( 0.0, 0.0, 0.0 );
Pyramid();
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
if( aspect == ASPECT_1_1 )
{
if( width < height && width > 0 )
glFrustum( left, right, bottom * height / width, top * height / width, near, far );
else
if( width >= height && height > 0 )
glFrustum( left * width / height, right * width / height, bottom, top, near, far );
}
else
glFrustum( left, right, bottom, top, near, far );
Display();
}
void Keyboard( unsigned char key, int x, int y )
{
if( key == '+' )
scale += 0.1;
else
if( key == '-' && scale > 0.1 )
scale -= 0.1;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}
void SpecialKeys( int key, int x, int y )
{
switch( key )
{
case GLUT_KEY_LEFT:
rotatey -= 1;
break;
case GLUT_KEY_UP:
rotatex -= 1;
break;
case GLUT_KEY_RIGHT:
rotatey += 1;
break;
case GLUT_KEY_DOWN:
rotatex += 1;
break;
}
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}
void MouseButton( int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON )
{
button_state = state;
if( state == GLUT_DOWN )
{
button_x = x;
button_y = y;
}
}
}
void MouseMotion( int x, int y )
{
if( button_state == GLUT_DOWN )
{
GLdouble model[ 16 ];
glGetDoublev( GL_MODELVIEW_MATRIX, model );
GLdouble proj[ 16 ];
glGetDoublev( GL_PROJECTION_MATRIX, proj );
GLint view[ 4 ];
glGetIntegerv( GL_VIEWPORT, view );
GLdouble prev[ 3 ], curr[ 3 ];
gluUnProject( button_x, button_y, 0.0, model, proj, view, prev + 0, prev + 1, prev + 2 );
gluUnProject( x, y, 0.0, model, proj, view, curr + 0, curr + 1, curr + 2 );
translatex += curr[ 0 ] - prev[ 0 ];
translatey += prev[ 1 ] - curr[ 1 ];
button_x = x;
button_y = y;
glutPostRedisplay();
}
}
void Menu( int value )
{
switch( value )
{
case FULL_WINDOW:
aspect = FULL_WINDOW;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case ASPECT_1_1:
aspect = ASPECT_1_1;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
glutInitWindowSize( 400, 400 );
glutCreateWindow( "Stos modelowania" );
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
glutKeyboardFunc( Keyboard );
glutSpecialFunc( SpecialKeys );
glutMouseFunc( MouseButton );
glutMotionFunc( MouseMotion );
int MenuAspect = glutCreateMenu( Menu );
#ifdef WIN32
glutAddMenuEntry( "Aspekt obrazu - całe okno", FULL_WINDOW );
#else
glutAddMenuEntry( "Aspekt obrazu - cale okno", FULL_WINDOW );
#endif
glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
glutCreateMenu( Menu );
glutAddSubMenu( "Aspekt obrazu", MenuAspect );
#ifdef WIN32
glutAddMenuEntry( "Wyjście", EXIT );
#else
glutAddMenuEntry( "Wyjscie", EXIT );
#endif
glutAttachMenu( GLUT_RIGHT_BUTTON );
glutMainLoop();
return 0;
}