Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Janusz Ganczarski
Biblioteki C++

Selekcja obiektów

[lekcja] Rozdział 21. Zmiana trybu renderowania, stos nazw obiektów, przetwarzanie rekordu trafień; program przykładowy.
Tryb selekcji jest jednym z trzech trybów renderingu dostępnym w bibliotece OpenGL. W trybie tym piksele nie są kopiowane do bufora ramki obrazu. W zamian za to w buforze selekcji tworzona jest lista prymitywów, których wierzchołki przecinają bryłę widzenia. Swoją budową bufor selekcji nie przypomina żadnego innego opisywanego bufora OpenGL - jest to po prostu jednowymiarowa tablica liczb całkowitych.

Zmiana trybu renderowania

Aby korzystać z selekcji obiektów w bibliotece OpenGL trzeba zmienić tryb renderingu. Służy do tego funkcja:
C/C++
GLint glRenderMode( GLenum mode )
której parametr mode przyjmuje następujące wartości:
  • GL_RENDER - tryb renderowania; jest to domyślny tryb renderingu,
  • GL_SELECT - tryb selekcji,
  • GL_FEEDBACK - tryb sprzężenia zwrotnego.
Wartość zwracana przez funkcję glRenderMode zależy od trybu renderingu. W przypadku trybu renderowania funkcja zwraca wartość 0. W trybie selekcji funkcja zwraca ilość rekordów trafień znajdujących się w buforze selekcji. Jeżeli zabraknie miejsca w buforze selekcji funkcja zwróci wartość -1. W ostatnim trybie sprzężenia zwrotnego funkcja zwraca ilość wartości zapisanych w buforze sprzężenia zwrotnego.
Tryb sprzężenia zwrotnego zostanie omówiony oddzielnie. W tym miejscu wspomnijmy jedynie, że w tym trybie piksele także nie są kopiowane do bufora ramki obrazu.

Stos nazw obiektów

Nazwy obiektów, które mogą zostać wybrane w trakcie selekcji, biblioteka OpenGL przechowuje na stosie. Jedną nazwą można opisać zarówno pojedynczy prymityw graficzny jak i dowolnie wybrana grupa obiektów.
Inicjalizację pustego stosu nazw obiektów wykonuje funkcja:
C/C++
void glInitNames()
Nazwy poszczególnym obiektom nadaje się wywołując funkcję:
C/C++
void glLoadName( GLuint name )
gdzie parametr name jest unikatowym identyfikatorem obiektu. Funkcja ta jednocześnie zastępuje nazwę znajdującą się aktualnie na szczycie stosu nazw.
Jeżeli chcemy wykrywać selekcję obiektów o hierarchicznej strukturze (np. elementy składowe złożonego obiektu) trzeba umieszczać kolejne nazwy obiektów na bieżącym stosie. Realizuje to funkcja:
C/C++
void glPushName( GLuint name )
której parametr name jest unikatowym identyfikatorem obiektu. Po zdefiniowaniu obiektu podrzędnego trzeba zdjąć nazwę obiektu ze stosu, co wymaga wywołania funkcji:
C/C++
void glPopName()
Wielkość stosu nazw jest określana przez implementację biblioteki, ale nie może być mniejsza niż 64. Niedomiar i przepełnienie stosu powoduje zgłoszenie błędów: GL_STACK_UNDERFLOW i GL_STACK_OVERFLOW. Aktualną głębokość stosu zwraca wywołanie funkcji z grupy glGet z parametrem GL_NAME_STACK_DEPTH, a ustalenie maksymalnej głębokości stosu wymaga użycie funkcji z tej samej grupy z parametrm GL_MAX_NAME_STACK_DEPTH.
Wszystkie powyższe operacje na stosie nazw są ignorowane, gdy biblioteka OpenGL znajduje się w innym trybie renderingu niż tryb selekcji.

Przetwarzanie rekordu trafień

Ostatnią operacją, którą trzeba wykonać przy korzystaniu z selekcji obiektów jest ustawienie bufora selekcji przechowującego dane obiektów wybranych w trakcie selekcji. Wymaga to wywołania funkcji:
C/C++
void glSelectBuffer( GLsizei size, GLuint * buffer )
gdzie parametr size określa wielkość bufora, do którego wskaźnik zawiera parametr buffer. Bufor powinien być na tyle duży, aby zmieścić informacje o wszystkich obiektach wybranych w wyniku selekcji, przy czym samo ustawienie bufora selekcji musi zostać wykonane przed przełączeniem biblioteki OpenGL w tryb selekcji. Przypomnijmy, że ilość wybranych obiektów zwraca funkcja glRenderMode w momencie powrotu do domyślnego trybu renderowania, czyli już po zakończeniu pracy w trybie selekcji.
Informacje o obiektach wybranych w trakcie selekcji zawarte są w tzw. rekordach trafień. Każdy rekord zawiera co najmniej cztery elementy (liczby całkowite ułożone kolejno w buforze selekcji) i ma następującą budowę:
  • [0] - ilość nazw na stosie nazw w momencie trafienia,
  • [1] - minimalna wartość współrzędnych z prymitywów graficznych wchodzących w skład wybranego obiektu; wartość ta jest pomnożona przez 232 − 1 i zaokrąglone do najbliższej liczby całkowitej,
  • [2] - maksymalna wartość współrzędnych z prymitywów graficznych wchodzących w skład wybranego obiektu; wartość ta jest pomnożona przez 232 − 1 i zaokrąglone do najbliższej liczby całkowitej,
  • [3] - najniższy element stosu nazw obiektów (pierwszy odłożony na stos),
  • [n] - najwyższy element stosu nazw obiektów (ostatni odłożony na stos), Bezpośrednio po ostatnim elemencie pierwszego rekordu trafień znajduje się drugi, a po nim kolejne rekordy.

Program przykładowy

W programie przykładowym (plik selekcja_obiektow.cpp) tworzymy cztery obiekty. Trzy z nich to sześciany, które należą do grupy CUBE. Każdy z sześcianów posiada swój własny identyfikator o nazwie odpowiadającej kolorowi (RED CUBE, GREEN CUBE i BLUE CUBE). Czwartym obiektem jest kula identyfikowana stałą o nazwie SPHERE.
Typowym przypadku, który został także zaimplementowany w prezentowanym programie, selekcję obiektów wykonuje się na podstawie położenia wskaźnika myszki, w chwili, gdy przycisk myszki został przyciśnięty. Aby uzyskać pożądany efekt trzeba ograniczyć bryłę odcinania tak aby obejmowała „najbliższą okolicę” punktu, w którym został przyciśnięty przycisk myszki. Najłatwiej jest to uzyskać korzystając z funkcji biblioteki GLU:
C/C++
void gluPickMatrix( GLdouble x, GLdouble y, GLdouble deltax, GLdouble deltay, const GLint viewport[ 4 ] )
Parametry x i y określają współrzędne środka nowej bryły odcinania (oczywiście we współrzędnych okienkowych). Kolejne parametry deltax i deltay określają szerokość i wysokość bryły odcinania w pikselach. Ostatni parametr viewport zawiera dane o rozmiarach bieżącego obszaru renderingu. Dane te najłatwiej jest pobrać wywołując funkcję glGetIntegerv z parametrem GL_VIEWPORT.
Jak ciekawostkę można podać w jaki sposób zaimplementowana została funkcja gluPickMatrix w bibliotece GLU autorstwa firmy SGI:
C/C++
glTranslatef(( viewport[ 2 ] - 2 *( x - viewport[ 0 ] ) ) / deltax,( viewport[ 3 ] - 2 *( y - viewport[ 1 ] ) ) / deltay, 0 );
glScalef( viewport[ 2 ] / deltax, viewport[ 3 ] / deltay, 1.0 );
Zanim jednak, korzystając z funkcji gluPickMatrix, zmodyfikujemy bryłę odcinania trzeba odłożyć oryginalną macierz modelowania na stos. Po zmodyfikowaniu bryły odcinania wykonujemy wszystkie etapy normalnego renderingu sceny, włączając w to przekształcenia macierzy modelowania, przy czym samo renderowanie sceny wykonywane jest już po włączeniu trybu selekcji. Po zakończeniu renderingu w trybie selekcji i ustaleniu ilości rekordów trafień trzeba dokonać analizy zawartości bufora selekcji.
Analiza bufora selekcji jest ściśle związana z ilością i układem hierarchii obiektów rysowanych na scenie. W przykładowym programie obiekty są tak zdefiniowane i ułożone, że w buforze selekcji mogą znaleźć się maksymalnie dwa rekordy trafień, stąd ich analiza jest stosunkowo prosta. Przyśpieszenie analizy ułatwia hierarchia obiektów sceny. Przykładowo wiedząc, że w pierwszym rekordzie trafień znajduje się kula, mamy pewność, że drugi rekord może zawierać wyłącznie sześcian.
Efekt działania przykładowego programu przedstawiają rysunki 1 i 2. W obu przypadkach komunikaty na dole okna pokazują ilość rekordów trafień oraz obiekt, który w miejscu trafienia znajdował się najbliżej położenia obserwatora.
Rysunek 1. Program Selekcja obiektów - wybrane dwa trafienia
Rysunek 1. Program Selekcja obiektów - wybrane dwa trafienia
Rysunek 2. Program Selekcja obiektów - wybrane jedno trafienie
Rysunek 2. Program Selekcja obiektów - wybrane jedno trafienie

Plik selekcja_obiektow.cpp

C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "colors.h"

// stałe do obsługi menu podręcznego

enum
{
    FULL_WINDOW, // aspekt obrazu - całe okno
    ASPECT_1_1, // aspekt obrazu 1:1
    EXIT // wyjście
};

// aspekt obrazu

int aspect = FULL_WINDOW;

// rozmiary bryły obcinania

const GLdouble left = - 2.0;
const GLdouble right = 2.0;
const GLdouble bottom = - 2.0;
const GLdouble top = 2.0;
const GLdouble near = 3.0;
const GLdouble far = 7.0;

// kąty obrotu

GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;

// wskaźnik naciśnięcia lewego przycisku myszki

int button_state = GLUT_UP;

// położenie kursora myszki

int button_x, button_y;

// współczynnik skalowania

GLfloat scale = 1.0;

// identyfikatory wyświetlanych obiektów

enum
{
    NONE,
    CUBE,
    RED_CUBE,
    GREEN_CUBE,
    BLUE_CUBE,
    SPHERE
};

// nazwa wybranego obiektu

char select_object[ 30 ] = "Trafienia: 0";

// funkcja rysująca napis w wybranym miejscu

void DrawString( GLfloat x, GLfloat y, char * string )
{
    // położenie napisu
    glRasterPos2f( x, y );
   
    // wyświetlenie napisu
    int len = strlen( string );
    for( int i = 0; i < len; i++ )
         glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
   
}

// funkcja generująca scenę 3D

void Display()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru i bufora głębokości
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // przesunięcie układu współrzędnych obiektu do środka bryły odcinania
    glTranslatef( 0, 0, -( near + far ) / 2 );
   
    // obroty obiektu
    glRotatef( rotatex, 1.0, 0.0, 0.0 );
    glRotatef( rotatey, 0.0, 1.0, 0.0 );
   
    // skalowanie obiektu - klawisze "+" i "-"
    glScalef( scale, scale, scale );
   
    // włączenie testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // włączenie oświetlenia
    glEnable( GL_LIGHTING );
   
    // włączenie światła GL_LIGHT0 z parametrami domyślnymi
    glEnable( GL_LIGHT0 );
   
    // włączenie automatycznej normalizacji wektorów normalnych
    glEnable( GL_NORMALIZE );
   
    // włączenie obsługi właściwości materiałów
    glEnable( GL_COLOR_MATERIAL );
   
    // inicjalizacja stosu nazw obiektów
    glInitNames();
   
    // umieszczenie nazwy na stosie nazw, aby nie był on pusty
    glPushName( NONE );
   
    // obiekt RED_CUBE z grupy CUBE
    glLoadName( CUBE );
    glPushName( RED_CUBE );
    glColor4fv( Red );
    glTranslatef( 0.0, 1.0, 0.0 );
    glutSolidCube( 0.5 );
    glPopName();
   
    // obiekt GREEN_CUBE z grupy CUBE
    glPushName( GREEN_CUBE );
    glColor4fv( Green );
    glTranslatef( 1.0, - 1.0, 0.0 );
    glutSolidCube( 0.5 );
    glPopName();
   
    // obiekt BLUE_CUBE z grupy CUBE
    glPushName( BLUE_CUBE );
    glColor4fv( Blue );
    glTranslatef( - 1.0, - 1.0, 0.0 );
    glutSolidCube( 0.5 );
    glPopName();
   
    // obiekt SPHERE, nie należący do grupy CUBE
    glLoadName( SPHERE );
    glColor4fv( Orange );
    glTranslatef( - 1.0, 1.0, 0.0 );
    glutSolidSphere( 0.4, 20, 20 );
   
    // wyłączenie oświetlenia
    glDisable( GL_LIGHTING );
   
    // wyłączenie testu bufora głębokości
    glDisable( GL_DEPTH_TEST );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // trzeba odpowiednio przekształcić układ współrzędnych
    // aby napis znajdował się na samej "górze" bryły obcinania
    glLoadIdentity();
    glTranslatef( 0, 0, - near );
   
    // narysowanie napisu o wybranym obiekcie
    glColor4fv( Black );
    DrawString( left + 0.02, bottom + 0.03, select_object );
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

void Reshape( int width, int height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, width, height );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // parametry bryły obcinania
    if( aspect == ASPECT_1_1 )
    {
        // wysokość okna większa od wysokości okna
        if( width < height && width > 0 )
             glFrustum( left, right, bottom * height / width, top * height / width, near, far );
        else
       
        // szerokość okna większa lub równa wysokości okna
        if( width >= height && height > 0 )
             glFrustum( left * width / height, right * width / height, bottom, top, near, far );
       
    }
    else
         glFrustum( left, right, bottom, top, near, far );
   
    // generowanie sceny 3D
    Display();
}

// obsługa selekcji obietków

void Selection( int x, int y )
{
    // wielkość bufora selekcji
    const int BUFFER_LENGTH = 64;
   
    // bufor selekcji
    GLuint select_buffer[ BUFFER_LENGTH ];
   
    // przygotowanie bufora selekcji
    glSelectBuffer( BUFFER_LENGTH, select_buffer );
   
    // pobranie obszaru rozmiaru renderingu
    int viewport[ 4 ];
    glGetIntegerv( GL_VIEWPORT, viewport );
   
    // szerokość i wysokość obszaru renderingu
    int width = viewport[ 2 ];
    int height = viewport[ 3 ];
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // odłożenie macierzy rzutowania na stos
    glPushMatrix();
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // parametry bryły obcinania - jednostkowa kostka dookoła punktu wskaźnika
    // myszy (x,y) rozciągającej się na dwa piksele w poziomie i w pionie
    gluPickMatrix( x, height - y, 2, 2, viewport );
   
    // parametry bryły obcinania
    if( aspect == ASPECT_1_1 )
    {
        // wysokość okna większa od wysokości okna
        if( width < height && width > 0 )
             glFrustum( left, right, bottom * height / width, top * height / width, near, far );
        else
       
        // szerokość okna większa lub równa wysokości okna
        if( width >= height && height > 0 )
             glFrustum( left * width / height, right * width / height, bottom, top, near, far );
       
    }
    else
         glFrustum( left, right, bottom, top, near, far );
   
    // włączenie trybu selekcji
    glRenderMode( GL_SELECT );
   
    // generowanie sceny 3D
    Display();
   
    // zliczenie ilości rekordów trafień, powrót do domyślnego trybu renderingu
    GLint hits = glRenderMode( GL_RENDER );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // zdjęcie macierzy rzutowania ze stosu
    glPopMatrix();
   
    // w wyniku slekcji nie wybrano żadnego obiektu
    if( hits == 0 )
         strcpy( select_object, "Trafienia: 0" );
   
    // w wyniku selekcji wybrano jeden obiekt
    if( hits == 1 )
    {
        // pojedyncza nazwa na stosie - kula
        // sprawdzenie drugiego warunku nie jest konieczne
        if( select_buffer[ 0 ] == 1 && select_buffer[ 3 ] == SPHERE )
        {
            strcpy( select_object, "Trafienia: 1, obiekt: SPHERE" );
        }
        else
        // dwie nazwy na stosie - jeden z trzech sześcianów
        // sprawdzenie drugiego warunku nie jest konieczne
        if( select_buffer[ 0 ] == 2 && select_buffer[ 3 ] == CUBE )
       
        // sprawdzenie wierzchołka stosu nazw
        switch( select_buffer[ 4 ] )
        {
            // czerwony sześcan
        case RED_CUBE:
            strcpy( select_object, "Trafienia: 1, obiekt: RED_CUBE" );
            break;
           
            // zielony sześcan
        case GREEN_CUBE:
            strcpy( select_object, "Trafienia: 1, obiekt: GREEN_CUBE" );
            break;
           
            // niebieski sześcan
        case BLUE_CUBE:
            strcpy( select_object, "Trafienia: 1, obiekt: BLUE_CUBE" );
            break;
        }
    }
   
    // w wyniku selekcji wybrano dwa obiekty
    if( hits == 2 )
    {
        // pierwszy obiekt - sześcian, drugi obiekt kula
        if( select_buffer[ 0 ] == 2 && select_buffer[ 8 ] == SPHERE )
        {
            // sprawdzenie, który z obiektów jest bliżej obserwatora
            if( select_buffer[ 2 ] > select_buffer[ 7 ] )
                 strcpy( select_object, "Trafienia: 2, pierwszy obiekt: SPHERE" );
            else
            // sprawdzenie, który sześcian jest bliżej obserwatora
            switch( select_buffer[ 4 ] )
            {
                // czerwony sześcan
            case RED_CUBE:
                strcpy( select_object, "Trafienia: 2, pierwszy obiekt: RED_CUBE" );
                break;
               
                // zielony sześcan
            case GREEN_CUBE:
                strcpy( select_object, "Trafienia: 2, pierwszy obiekt: GREEN_CUBE" );
                break;
               
                // niebieski sześcan
            case BLUE_CUBE:
                strcpy( select_object, "Trafienia: 2, pierwszy obiekt: BLUE_CUBE" );
                break;
            }
        }
        else
        // pierwszy obiekt - sześcian, drugi obiekt także sześcian
        // z uwagi na kolejność definiowania obiektów przypadek,
        // gdy pierwszym obiektem jest kula a drugim sześcian nie wystąpi
        if( select_buffer[ 0 ] == 2 && select_buffer[ 8 ] == CUBE )
        {
            // sprawdzenie, który z sześcianów jest bliżej obserwatora
            if( select_buffer[ 2 ] > select_buffer[ 7 ] )
           
            // drugi sześcian jest bliżej
            switch( select_buffer[ 9 ] )
            {
                // czerwony sześcan
            case RED_CUBE:
                strcpy( select_object, "Trafienia: 2, pierwszy obiekt: RED_CUBE" );
                break;
               
                // zielony sześcan
            case GREEN_CUBE:
                strcpy( select_object, "Trafienia: 2, pierwszy obiekt: GREEN_CUBE" );
                break;
               
                // niebieski sześcan
            case BLUE_CUBE:
                strcpy( select_object, "Trafienia: 2, pierwszy obiekt: BLUE_CUBE" );
                break;
            }
            else
           
            // pierwszy sześcian jest bliżej
            switch( select_buffer[ 4 ] )
            {
                // czerwony sześcan
            case RED_CUBE:
                strcpy( select_object, "Trafienia: 2, pierwszy obiekt: RED_CUBE" );
                break;
               
                // zielony sześcan
            case GREEN_CUBE:
                strcpy( select_object, "Trafienia: 2, pierwszy obiekt: GREEN_CUBE" );
                break;
               
                // niebieski sześcan
            case BLUE_CUBE:
                strcpy( select_object, "Trafienia: 2, pierwszy obiekt: BLUE_CUBE" );
                break;
            }
        }
    }
}

// obsługa klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    // klawisz +
    if( key == '+' )
         scale += 0.05;
    else
   
    // klawisz -
    if( key == '-' && scale > 0.05 )
         scale -= 0.05;
   
    // narysowanie sceny
    Display();
}

// obsługa klawiszy funkcyjnych i klawiszy kursora

void SpecialKeys( int key, int x, int y )
{
    switch( key )
    {
        // kursor w lewo
    case GLUT_KEY_LEFT:
        rotatey -= 1;
        break;
       
        // kursor w górę
    case GLUT_KEY_UP:
        rotatex -= 1;
        break;
       
        // kursor w prawo
    case GLUT_KEY_RIGHT:
        rotatey += 1;
        break;
       
        // kursor w dół
    case GLUT_KEY_DOWN:
        rotatex += 1;
        break;
    }
   
    // odrysowanie okna
    Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}

// obsługa przycisków myszki

void MouseButton( int button, int state, int x, int y )
{
    if( button == GLUT_LEFT_BUTTON )
    {
        // obsługa selekcji obiektów
        Selection( x, y );
        glutPostRedisplay();
       
        // zapamiętanie stanu lewego przycisku myszki
        button_state = state;
       
        // zapamiętanie położenia kursora myszki
        if( state == GLUT_DOWN )
        {
            button_x = x;
            button_y = y;
        }
    }
}

// obsługa ruchu kursora myszki

void MouseMotion( int x, int y )
{
    if( button_state == GLUT_DOWN )
    {
        rotatey += 30 *( right - left ) / glutGet( GLUT_WINDOW_WIDTH ) *( x - button_x );
        button_x = x;
        rotatex -= 30 *( top - bottom ) / glutGet( GLUT_WINDOW_HEIGHT ) *( button_y - y );
        button_y = y;
        glutPostRedisplay();
       
        // obsługa selekcji obiektów
        Selection( x, y );
    }
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // obszar renderingu - całe okno
    case FULL_WINDOW:
        aspect = FULL_WINDOW;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // obszar renderingu - aspekt 1:1
    case ASPECT_1_1:
        aspect = ASPECT_1_1;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
    }
}

int main( int argc, char * argv[] )
{
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
   
    glutCreateWindow( "Selekcja obiektów" );
    #else
   
    glutCreateWindow( "Selekcja obiektow" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( Display );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // dołączenie funkcji obsługi klawiatury
    glutKeyboardFunc( Keyboard );
   
    // dołączenie funkcji obsługi klawiszy funkcyjnych i klawiszy kursora
    glutSpecialFunc( SpecialKeys );
   
    // obsługa przycisków myszki
    glutMouseFunc( MouseButton );
   
    // obsługa ruchu kursora myszki
    glutMotionFunc( MouseMotion );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // utworzenie podmenu - aspekt obrazu
    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 );
   
    // menu główne
    glutCreateMenu( Menu );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    #ifdef WIN32
   
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującej menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokument Następny dokument
Tekstury Sprzężenie zwrotne