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

Tablice wierzchołków

[lekcja] Rozdział 23. Definiowanie i renderowanie danych, tablice przeplatane, tablice indeksowe, pobieranie adresów tablic, stos atrybutów klienta OpenGL; dwa programy przykładowe.
Tablice wierzchołków (ang vertex array) zostały wprowadzone w wersji 1.1 biblioteki OpenGL. Wcześniej technikę tę udostępniało rozszerzenie EXT vertex array. Pod nieco mylącą nazwą ukrywa się mechanizm pozwalający na zupełnie inne podejście do tworzenia grafiki. Dane obejmujące m.in. współrzędne wierzchołków prymitywów i dane kolorów wierzchołków przechowywane są w jednej lub kilku tablicach i renderowane bezpośrednio z pominięciem mechanizmu glBegin/glEnd.
Mechanizm tablic wierzchołków jest tym bardziej elastyczny, ponieważ dane przechowywane w tablicach (tablicy) można w dowolny sposób modyfikować. Stanowi to istotną różnicę w stosunku do list wyświetlania, których głównym ograniczeniem jest statyczność.

Włączenie tablic wierzchołków

Korzystanie z tablic wierzchołków odbywa się w kilku etapach. Na początku musimy włączyć tablice (wszystkie tablice są domyślnie nieaktywne), które będą używane przy renderingu sceny. Realizuje to funkcja:
C/C++
void glEnableClientState( GLenum array )
której parametr array przyjmuje jedną z poniższych wartości:
  • GL_VERTEX_ARRAY - współrzędne wierzchołków prymitywów,
  • GL_NORMAL_ARRAY - współrzędne wektorów normalnych,
  • GL_COLOR_ARRAY - składowe kolorów wierzchołków,
  • GL_SECONDARY_COLOR_ARRAY - składowe drugorzędnych kolorów wierzchołków,
  • GL_INDEX_ARRAY - indeksy kolorów,
  • GL_EDGE_FLAG_ARRAY - znaczniki krawędzi wielokątów,
  • GL_FOG_COORD_ARRAY - współrzędne mgły,
  • GL_TEXTURE_COORD_ARRAY - współrzędne tekstur.
Stała GL_SECONDARY_COLOR_ARRAY została wprowadzona w wersji 1.4 biblioteki OpenGL, a wcześniej w rozszerzeniu EXT secondary color.
Stała GL_FOG_COORD_ARRAY została wprowadzona w wersji 1.4 biblioteki OpenGL, przy czym jej pierwotna nazwa to GL_FOG_COORDINATE_ARRAY (nazwę tej i kilku innych stałych zmieniono w specyfikacji wersji 1.5 biblioteki). Wcześniej obsługę tablic współrzędnych mgły definiowało rozszerzenie EXT fog coord.
Wyłączenie wybranej tablicy wierzchołków sprowadza się do wywołania funkcji:
C/C++
void glDisableClientState( GLenum array )
której parametr array przyjmuje identyczne wartości jak w funkcji glEnableClientState.

Definiowanie danych

Drugim etapem przy tworzeniu tablic wierzchołków jest zdefiniowanie danych, czyli odpowiednie powiązanie przygotowanych w programie tablic z danymi. Dla każdego rodzaju danych wykorzystywana jest do tego odrębna funkcja, ale mają one ten sam zbiór parametrów:
C/C++
void glVertexPointer( GLint size, GLenum type, GLsizei stride, const void * pointer )
void glNormalPointer( GLenum type, GLsizei stride, const void * pointer )
void glColorPointer( GLint size, GLenum type, GLsizei stride, const GLvoid * pointer )
void glSecondaryColorPointer( GLint size, GLenum type, GLsizei stride, const GLvoid * pointer )
void glIndexPointer( GLenum type, GLsizei stride, const GLvoid * pointer )
void glEdgeFlagPointer( GLsizei stride, const GLvoid * pointer )
void glFogCoordPointer( GLenum type, GLsizei stride, const GLvoid * pointer )
void glTexCoordPointer( GLint size, GLenum type, GLsizei stride, const GLvoid * pointer )
Parametr size (nie występujący w części funkcji) określa liczbę składowych w elemencie tablicy. Przykładowo współrzędne wierzchołków prymitywów (glVertexPointer) mogą być opisywane za pomocą dwóch, trzech lub czterech liczb. Natomiast do zdefiniowania wektora normalnego potrzeba zawsze trzech współrzędnych, stąd np. w funkcji glNormalPointer parametr size nie występuje.
Parametr type określa typ danych zawartych w tablicy. Dopuszczalne wartości zależą od rodzaju definiowanych danych. Zestawienie dopuszczalnych wartości tego parametru oraz parametru size dla poszczególnych funkcji definiujących dane zawiera tabela 1. Jedynym wyjątkiem nie posiadającym tego parametru jest funkcja glEdgeFlagPointer. Jest to związane z tym, że dane o znacznikach krawędzi wierzchołków przechowywane są wyłącznie przy użyciu typu GLboolean.
Przypomnijmy, że stałe GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT i GL_DOUBLE odpowiadają następującym typom danych w bibliotece OpenGL: GLbyte, GLubyte, GLshort, GLushort, GLint, GLuint, GLfloat i GLdouble. Co ciekawe, pomimo iż typ GLdouble jest dostępny w bibliotece OpenGL od początku, stała GL_DOUBLE została wprowadzona dopiero w wersji 1.1, właśnie w związku z tablicami wierzchołków.

Tabela 1: Zestawienie parametrów funkcji definiujących dane do tablic wierzchołków

funkcjasizetype
glVertexPointer2,3,4GL_SHORT, GL_INT, GL_FLOAT,
GL_DOUBLE
glNormalPointer3GL_BYTE, GL_SHORT, GL_INT,
GL_FLOAT, GL_DOUBLE
glColorPointer3,4GL_BYTE, GL_UNSIGNED_BYTE,
GL_SHORT, GL_UNSIGNED_SHORT,
GL_INT, GL_UNSIGNED_INT,
GL_FLOAT, GL_DOUBLE
glSecondaryColorPointer3GL_BYTE, GL_UNSIGNED_BYTE,
GL_SHORT, GL_UNSIGNED_SHORT,
GL_INT, GL_UNSIGNED_INT,
GL_FLOAT, GL_DOUBLE
glIndexPointer1GL_UNSIGNED_BYTE, GL_SHORT,
GL_INT, GL_FLOAT, GL_DOUBLE
glEdgeFlagPointer1-
glFogCoordPointer1GL_FLOAT, GL_DOUBLE
glTexCoordPointer1,2,3,4GL_SHORT, GL_INT, GL_FLOAT,
GL_DOUBLE
Współrzędne wektorów normalnych, kolorów wierzchołków i drugorzędnych kolorów wierzchołków reprezentowane liczbami stałoprzecinkowymi bez znaku (GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT i GL_UNSIGNED_INT) są normalizowane do przedziału [0, 1]. Podobna zasada dotyczy danych reprezentowanych liczbami stałoprzecinkowymi ze znakiem (GL_BYTE, GL_SHORT i GL_INT), które są normalizowane do przedziału [−1, 1].
Kolejny parametr stride definiuje przesunięcie w bajtach pomiędzy poszczególnymi elementami tablicy. Umożliwia to zdefiniowanie jednej „dużej” tablicy zawierającej jednocześnie np. współrzędne wierzchołków prymitywów, dane o ich kolorach oraz wektorach normalnych.
Ostatni parametr pointer to oczywiście wskaźnik pierwszy element tablicy z właściwymi danymi.
W przypadku, gdy korzystamy techniki wieloteksturowania, dla każdej z jednostek teksturujących definiujemy odrębną tablicę współrzędnych (może to być fizycznie ta sama tablica). Zanim jednak przystąpimy do definiowania tablicy współrzędnych trzeba wybrać numer aktywnej jednostki teksturującej. Realizuje to funkcja:
C/C++
void glClientActiveTexture( GLenum texture )
której parametr texture określa numer aktywnej jednostki (stała GL_TEXTURE0 i następne). Domyślnie aktywna jest pierwsza jednostka teksturująca.
Przypomnijmy jeszcze, że zanim technika wieloteksturowania została włączona do wersji 1.3 specyfikacji biblioteki OpenGL, została opisana w rozszerzeniach: SGIS multitexture, EXT multitexture i ARB multitexture.

Tablice przeplatane

W przypadku, gdy korzystamy z tablicy zawierającej przeplatane dane różnego rodzaju, możemy skorzystać z funkcji, która jednocześnie włącza tablice wierzchołków jak też i definiuje odpowiednie dane:
C/C++
void glInterleavedArrays( GLenum format, GLsizei stride, const GLvoid * pointer )
Jedynym ograniczeniem przy korzystaniu z tej funkcji jest ściśle określona lista formatów w jakich mogą być ułożone dane w tablicy. Format danych tablicy przekazywany jest w parametrze format i przyjmuje jedną z poniższych wartości:
  • GL_V2F - współrzędne wierzchołków prymitywów (2 × GLfloat),
  • GL_V3F - współrzędne wierzchołków prymitywów (3 × GLfloat),
  • GL_C4UB_V2F - składowe kolorów wierzchołków (4 × GLubyte) i współrzędne wierzchołków prymitywów (2 × GLfloat),
  • GL_C4UB_V3F - składowe kolorów wierzchołków (4 × GLubyte) i współrzędne wierzchołków prymitywów (3 × GLfloat),
  • GL_C3F_V3F - składowe kolorów wierzchołków (3 × GLfloat) i współrzędne wierzchołków prymitywów (3 × GLfloat),
  • GL_N3F_V3F - współrzędne wektorów normalnych (3 × GLfloat) i współrzędne wierzchołków prymitywów (3 × GLfloat),
  • GL_C4F_N3F_V3F - składowe kolorów wierzchołków (4 × GLfloat), współrzędne wektorów normalnych (3 × GLfloat) i współrzędne wierzchołków prymitywów (3 × GLfloat),
  • GL_T2F_V3F - współrzędne tekstur (2 × GLfloat) i współrzędne wierzchołków prymitywów (3 × GLfloat),
  • GL_T4F_V4F - współrzędne tekstur (4 × GLfloat) i współrzędne wierzchołków prymitywów (4 × GLfloat),
  • GL_T2F_C4UB_V3F - współrzędne tekstur (2 × GLfloat), składowe kolorów wierzchołków (4 × GLubyte) i współrzędne wierzchołków prymitywów (3 × GLfloat),
  • GL_T2F_C3F_V3F - współrzędne tekstur (2 × GLfloat), składowe kolorów wierzchołków (3 × GLfloat), i współrzędne wierzchołków prymitywów (3 × GLfloat),
  • GL_T2F_N3F_V3F - współrzędne tekstur (2 × GLfloat), współrzędne wektorów normalnych (3 × GLfloat) i współrzędne wierzchołków prymitywów (3 × GLfloat),
  • GL_T2F_C4F_N3F_V3F - współrzędne tekstur (2 × GLfloat), składowe kolorów wierzchołków (4 × GLfloat), współrzędne wektorów normalnych (3 × GLfloat) i współrzędne wierzchołków prymitywów (3 × GLfloat),
  • GL_T4F_C4F_N3F_V4F - współrzędne tekstur (2 × GLfloat), składowe kolorów wierzchołków (4 × GLfloat), współrzędne wektorów normalnych (3 × GLfloat) i współrzędne wierzchołków prymitywów (4 × GLfloat).
Jak Czytelnik zapewne zauważył rodzaj danych zawartych w tablicy jednoznacznie określa nazwa stałej. Przy definiowaniu tablic zawierających dane różnego typu trzeba pamiętać, że dane opisane czterema liczbami typu GLubyte są wyrównywane do najbliższej wielokrotności liczby typu GLfloat.
Pozostałe parametry stride i pointer to oczywiście określone w bajtach przesunięcie pomiędzy elementami tablicy i wskaźnik na tablicę z danymi.

Renderowanie danych

Ostatnim etapem przy korzystaniu z tablic wierzchołków jest renderowanie danych znajdujących się w tablicach. Biblioteka OpenGL udostępnia do tego celu kilka funkcji.
Pierwsza funkcja pobiera dane tylko z jednej pozycji tablic wierzchołków:
C/C++
void glArrayElement( GLint i )
Efekt jej użycia nie różni się od wywołania jednej lub kilku funkcji (w zależności od ilości zdefiniowanych danych) określającej wybrane dane. Ponieważ OpenGL nie posiada w tym przypadku informacji o rodzaju renderowanego prymitywu wywołanie funkcji glArrayElement najczęściej umieszcza się pomiędzy wywołaniami funkcji glBegin/glEnd.
Druga funkcja służy do rysowania określonych prymitywów graficznych z danych znajdujących się w tablicach wierzchołków:
C/C++
void glDrawArrays( GLenum mode, GLint first, GLsizei count )
Parametr mode określa rodzaj rysowanego prymitywu graficznego i przyjmuje takie same wartości jak parametr mode funkcji glBegin. Są to są dobrze już znane stałe: GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, GL_QUAD_STRIP, GL_QUADS i GL_POLYGON.
Parametr first wskazuje pierwszy element tablic wierzchołków, który ma zostać wykorzystany do renderingu, a parametr count określa ilość renderowanych elementów.
W wersji 1.4 biblioteki OpenGL, a wcześniej w rozszerzeniach EXT multi draw arrays i SUN multi draw arrays, wprowadzono funkcję pozwalającą na rysowanie ciągu dowolnie wybranych elemetów tablic wierzchołków:
C/C++
void glMultiDrawArrays( GLenum mode, GLint * first, GLsizei * count, GLsizei primcount )
Parametr mode określa rodzaj rysowanego prymitywu i oczywiście przyjmuje takie same wartości jak parametr mode funkcji glDrawArrays. Tablice wskazywane przez parametry first i count zawierają kolejne numery pierwszego renderowanego elementu tablicy wierzchołków i ilość renderowanych elementów. Wielkość tak opisanego podzbioru elementów tablic wierzchołków określa parametr primcount.

Indeksowe tablice wierzchołków

Z indeksowego opisu bryły korzystaliśmy po raz pierwszy w programie prezentującym sposób obliczania wektorów normalnych. Jedna tablica zawiera dane opisujące poszczególne wierzchołki bryły. Druga tablica zawiera opis budowy poszczególnych wierzchołków - czyli indeksy do tablicy z danymi wierzchołków. Ta technika pozwala na zmniejszenie ilości danych, bowiem każdy wierzchołek jest opisany tylko raz, a nie wielokrotnie jak to ma miejsce w standardowym opisie obiektu.
Biblioteka OpenGL udostępnia kilka funkcji obsługujących indeksowe tablice wierzchołków. Pierwsza z nich renderuje prymitywy graficzne z wierzchołkami określonymi w tablicach wierzchołków:
C/C++
void glDrawElements( GLenum mode, GLsizei count,
GLenum type, const GLvoid * indices )
Parametr mode określa rodzaj rysowanego prymitywu graficznego (wartości takie same jak w funkcji glBegin). Drugi parametr count zawiera ilość indeksów znajdujących się w tablicy indeksów, do której wskaźnik zawiera parametr indices. Format danych tablicy indeksów określa parametr type, który przyjmuje jedną z wartości: GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT i GL_UNSIGNED_INT. Indeksy numerowane są od 0.
W wersji 1.2 biblioteki OpeneGL, a wcześniej w rozszerzeniu EXT draw - range elements, dodano funkcję:
C/C++
void glDrawRangeElements( GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid * indices )
która różni się od funkcji glDrawElements dwoma dodatkowymi parametrami start i end określającymi minimalną i maksymalną wartość wykorzystywanych indeksów tablic wierzchołków. Pozostałe parametry mają takie same znaczenie jak odpowiadające im parametry funkcji glDrawElements.
Intencją wprowadzenia tej funkcji było umożliwienie optymalizacji wyświetlania indeksowych tablic wierzchołków. W tym celu dodano dwie nowe zależne od implementacji stałe określające rekomendowane przez implementację:
  • GL_MAX_ELEMENTS_VERTICES - maksymalną ilość elementów tablic wierzchołków,
  • GL_MAX_ELEMENTS_INDICES - maksymalną ilość indeksów tablic wierzchołków,
przy których istnieje możliwość przyśpieszenia operacji na indeksowanych tablicach wierzchołków. Wartości stałych można uzyskać np. przy użyciu funkcji glGetInteger.
W wersji 1.4 biblioteki OpenGL, a wcześniej w rozszerzeniach EXT multi draw arrays i SUN multi draw arrays, wprowadzono funkcję pozwalającą na renderowanie prymitywów przy użyciu kilku tablic indeksów:
C/C++
void glMultiDrawElements( GLenum mode, const GLsizei * count, GLenum type, const GLvoid ** indices, GLsizei primcount )
Parametr mode określa rodzaj rysowanego prymitywu i oczywiście przyjmuje takie same wartości jak parametr mode funkcji glDrawElements. Tablica wskazywana przez parametr count zawierają ilości elementów kolejnych tablic indeksów, które przekazywane są w parametrze indices. Ilość tablic indeksów określa parametr primcount. Wszystkie tablice indeksów zawierają dane określone w parametrze type, który przyjmuje takie same wartości jak odpowiadający mu parametr funkcji glDrawElements.

Pobieranie adresów tablic

Pobieranie adresów tablic wierzchołków oraz buforów selecji i sprzężenia zwrotnego umożliwia funkcja:
C/C++
void glGetPointerv( GLenum pname, GLvoid ** params )
której parametr pname określa rodzaj wskaźnika zwracanego w parametrze params:
  • GL_COLOR_ARRAY_POINTER - wskaźnik do tablicy ze składowymi kolorów wierzchołków,
  • GL_EDGE_FLAG_ARRAY_POINTER - wskaźnik do tablicy ze znacznikami krawędzi wielokątów,
  • GL_FEEDBACK_BUFFER_POINTER - wskaźnik do bufora sprzężenia zwrotnego,
  • GL_INDEX_ARRAY_POINTER - wskaźnik do tablicay z indeksami kolorów,
  • GL_NORMAL_ARRAY_POINTER - wskaźnik do tablicy ze współrzędnymi wektorów normalnych,
  • GL_TEXTURE_COORD_ARRAY_POINTER - wskaźnik do tablicy ze współrzędnymi tekstur,
  • GL_SELECTION_BUFFER_POINTER - wskaźnik do bufora selekcji,
  • GL_VERTEX_ARRAY_POINTER - wskaźnik do tablicy ze współrzędnymi wierzchołków prymitywów,
  • GL_SECONDARY_COLOR_ARRAY_POINTER - wskaźnik do tablicy ze składowymi drugorzędnych kolorów wierzchołków,
  • GL_FOG_COORD_ARRAY_POINTER - wskaźnik do tablicy ze współrzędnymi mgły.

Stos atrybutów klienta OpenGL

Zmienne stanu, czyli tzw. atrybuty, związane z tablicami wierzchołków, a także atrybuty związane z przetwarzaniem pikseli przechowywane są po stronie klienta OpenGL, w odróżnieniu od opisanych wcześniej grup zmiennych stanu przechowywanych po stronie serwera OpenGL (odcinek kursu poświęcony materiałom i oświetleniu). Atrybyty przechowywane po stronie klienta także można odkłądac na stos korzystając z funkcji:
C/C++
void glPushClientAttrib( GLbitfield mask )
której parametr mask, to maska bitowa określająca jaka grupa lub grupy zmiennych stanu mają zostać odłożone na stos. Możliwe są następujące wartości:
  • GL_CLIENT_PIXEL_STORE_BIT - atrybuty przetwarzania pikseli,
  • GL_CLIENT_VERTEX_ARRAY_BIT - zmienne stanu związane z tablicami wierzchołków,
  • GL_CLIENT_ALL_ATTRIB_BITS - wszystkie atrybuty znajdujące się po stronie klienta OpenGL.
Przywrócenie wartości wcześniej odłożonych na stos realizuje funkcja:
C/C++
void glPopClientAttrib()

Programy przykładowe

Pierwszy program przykładowy (plik tablice_wierzcholkow.cpp) testuje wydajność tablic wierzchołków w porównaniu z listami wyświetlania. Przeprowadzone testy dały dość zaskakujące wyniki. Okazało się, że przy renderingu punktów listy wyświetlania są szybsze od tablic wierzchołków (patrz rysunki nr 1 i 2). Natomiast przy rysowaniu odcinków i trójkątów różnice w szybkości renderingu praktycznie nie występują.
Podobieństwo okien programu do wcześniej prezentowanego programu testującego wydajność list wyświetlania nie jest przypadkowa, bowiem opisywany program jest jego niewielką przeróbką. Zasadnicza różnica polega na sposobie przechowywania danych o wierzchołkach. Dane o współrzędnych wierzchołków prymitywów oraz o składowych kolorów zebrano w dwóch odrębnych tablicach, a nie sześciu jak było w pierwotnym programie.
Rysunek 1. Program Tablice wierzchołków - test rysowania punktów z listą wyświetlania
Rysunek 1. Program Tablice wierzchołków - test rysowania punktów z listą wyświetlania
Rysunek 2. Program Tablice wierzchołków - test rysowania punktów z tablicami wierzchołków
Rysunek 2. Program Tablice wierzchołków - test rysowania punktów z tablicami wierzchołków

Plik tablice_wierzcholkow.cpp

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

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

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

enum
{
    // rodzaj testu
    POINTS, // punkty bez listy wyświetlania
    POINTS_DL, // punkty z listą wyświetlania
    POINTS_VA, // punkty z tablicami wierzchołków
    LINES, // odcinki bez listy wyświetlania
    LINES_DL, // odcinki z listą wyświetlania
    LINES_VA, // odcinki z tablicami wierzchołków
    TRIANGLES, // trójkąty bez listy wyświetlania
    TRIANGLES_DL, // trójkąty z listą wyświetlania
    TRIANGLES_VA, // trójkąty z tablicami wierzchołków
   
    // obszar renderingu
    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;

// rodzaj testu - domyślnie punkty bez listy wyświetlania

int test = POINTS;

// identyfikator pierwszej listy wyświetlania

GLuint listid;

// ilość generowanych współrzędnych prymitywów

const int size = 30000;

// współrzędne prymitywów

GLfloat vertex_xyz[ 3 * size ];

// kolory prymitywów

GLfloat color_rgb[ 3 * size ];

// licznik ramek (FPS)

int frames = 0;

// licznik czasu

long start_time = 0;

// tablica znaków ze wartością FPS

char time_string[ 100 ] = "FPS:";

// 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 ] );
   
}

// test renderingu punktów

void Points()
{
    glBegin( GL_POINTS );
    for( int i = 0; i < size; i++ )
    {
        glColor3fv( color_rgb + 3 * i );
        glVertex3fv( vertex_xyz + 3 * i );
    }
    glEnd();
}

// test renderingu odcinków

void Lines()
{
    glBegin( GL_LINES );
    for( int i = 0; i < size / 2; i++ )
    {
        glColor3fv( color_rgb + 3 * 2 * i + 0 );
        glVertex3fv( vertex_xyz + 3 * 2 * i + 0 );
        glColor3fv( color_rgb + 3 * 2 * i + 3 );
        glVertex3fv( vertex_xyz + 3 * 2 * i + 3 );
    }
    glEnd();
}

// test renderingu trójkątów

void Triangles()
{
    glBegin( GL_TRIANGLES );
    for( int i = 0; i < size / 3; i++ )
    {
        glColor3fv( color_rgb + 3 * 3 * i + 0 );
        glVertex3fv( vertex_xyz + 3 * 3 * i + 0 );
        glColor3fv( color_rgb + 3 * 3 * i + 3 );
        glVertex3fv( vertex_xyz + 3 * 3 * i + 3 );
        glColor3fv( color_rgb + 3 * 3 * i + 6 );
        glVertex3fv( vertex_xyz + 3 * 3 * i + 6 );
    }
    glEnd();
}

// funkcja generująca scenę 3D

void DisplayScene()
{
    // licznik czasu
    if( !frames++ )
         start_time = clock();
   
    // 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 );
   
    // włączenie testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // wykonanie wybranego testu
    switch( test )
    {
        // punkty bez listy wyświetlania
    case POINTS:
        Points();
        break;
       
        // punkty z listą wyświetlania
    case POINTS_DL:
        glCallList( listid + 0 );
        break;
       
        // odcinki bez listy wyświetlania
    case LINES:
        Lines();
        break;
       
        // odcinki z listą wyświetlania
    case LINES_DL:
        glCallList( listid + 1 );
        break;
       
        // trójkąty bez listy wyświetlania
    case TRIANGLES:
        Triangles();
        break;
       
        // trójkąty z listą wyświetlania
    case TRIANGLES_DL:
        glCallList( listid + 2 );
        break;
       
        // prymitywy z tablicami wierzchołków
    case POINTS_VA:
    case LINES_VA:
    case TRIANGLES_VA:
       
        // włączenie tablic wierzchołków
        glEnableClientState( GL_VERTEX_ARRAY );
        glEnableClientState( GL_COLOR_ARRAY );
       
        // zdefiniowanie danych tablic wierzchołków
        glVertexPointer( 3, GL_FLOAT, 0, vertex_xyz );
        glColorPointer( 3, GL_FLOAT, 0, color_rgb );
       
        // narysowanie danych zawartych w tablicach
        if( test == POINTS_VA )
             glDrawArrays( GL_POINTS, 0, size );
        else
        if( test == LINES_VA )
             glDrawArrays( GL_LINES, 0, size );
        else
             glDrawArrays( GL_TRIANGLES, 0, size );
       
        // wyłączenie tablic wierzchołków
        glDisableClientState( GL_VERTEX_ARRAY );
        glDisableClientState( GL_COLOR_ARRAY );
        break;
    };
   
    // 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 );
   
    // komunikat o ilości ramek rysowanych na sekundę (FPS)
    glColor3fv( Black );
    if( frames == 250 )
    {
        frames = 0;
        sprintf( time_string, "FPS: %i",( int )( 250 * CLOCKS_PER_SEC /( float )( clock() - start_time ) ) );
    }
   
    // narysowanie napisu
    DrawString( left, bottom, time_string );
   
    // 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
    DisplayScene();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // wybór rodzaju testu
    case POINTS:
    case POINTS_DL:
    case POINTS_VA:
    case LINES:
    case LINES_DL:
    case LINES_VA:
    case TRIANGLES:
    case TRIANGLES_DL:
    case TRIANGLES_VA:
        test = value;
        DisplayScene();
        break;
       
        // 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 );
    }
}

// utworzenie list wyświetlania

void GenerateDisplayLists()
{
    // określenie zarodka dla  ciągu liczb pseudolosowych
    srand( time( NULL ) );
   
    // generowanie położenia punktów (wierzchołków) figur oraz ich kolorów
    for( int i = 0; i < size; i++ )
    {
        vertex_xyz[ 3 * i + 0 ] =( rand() /( float ) RAND_MAX ) * 4 - 2;
        vertex_xyz[ 3 * i + 1 ] =( rand() /( float ) RAND_MAX ) * 4 - 2;
        vertex_xyz[ 3 * i + 2 ] =( rand() /( float ) RAND_MAX ) * 4 - 2;
        color_rgb[ 3 * i + 0 ] =( rand() /( float ) RAND_MAX );
        color_rgb[ 3 * i + 1 ] =( rand() /( float ) RAND_MAX );
        color_rgb[ 3 * i + 2 ] =( rand() /( float ) RAND_MAX );
    }
   
    // wygenerowanie identyfikatorów trzech list wyświetlania
    listid = glGenLists( 3 );
   
    // utworzenie pierwszej listy - test rysowania punktów
    glNewList( listid + 0, GL_COMPILE );
    Points();
    glEndList();
   
    // utworzenie drugiej listy - test rysowania odcinków
    glNewList( listid + 1, GL_COMPILE );
    Lines();
    glEndList();
   
    // utworzenie pierwszej listy - test rysowania trójkątów
    glNewList( listid + 2, GL_COMPILE );
    Triangles();
    glEndList();
}

int main( int argc, char * argv[] )
{
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
   
    glutCreateWindow( "Tablice wierzchołków" );
    #else
   
    glutCreateWindow( "Tablice wierzcholkow" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( DisplayScene );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // utworzenie podmenu - Test
    int MenuTest = glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Punkty bez listy wyświetlania", POINTS );
    glutAddMenuEntry( "Punkty z listą wyświetlania", POINTS_DL );
    glutAddMenuEntry( "Punkty z tablicami wierzchołków", POINTS_VA );
    glutAddMenuEntry( "Odcinki bez listy wyświetlania", LINES );
    glutAddMenuEntry( "Odcinki z listą wyświetlania", LINES_DL );
    glutAddMenuEntry( "Odcinki z tablicami wierzchołków", LINES_VA );
    glutAddMenuEntry( "Trójkąty bez listy wyświetlania", TRIANGLES );
    glutAddMenuEntry( "Trójkąty z listą wyświetlania", TRIANGLES_DL );
    glutAddMenuEntry( "Trójkąty z tablicami wierzchołków", TRIANGLES_VA );
    #else
   
    glutAddMenuEntry( "Punkty bez listy wyswietlania", POINTS );
    glutAddMenuEntry( "Punkty z lista wyswietlania", POINTS_DL );
    glutAddMenuEntry( "Punkty z tablicami wierzcholkow", POINTS_VA );
    glutAddMenuEntry( "Odcinki bez listy wyswietlania", LINES );
    glutAddMenuEntry( "Odcinki z lista wyswietlania", LINES_DL );
    glutAddMenuEntry( "Odcinki z tablicami wierzcholkow", LINES_VA );
    glutAddMenuEntry( "Trojkaty bez listy wyswietlania", TRIANGLES );
    glutAddMenuEntry( "Trojkaty z lista wyswietlania", TRIANGLES_DL );
    glutAddMenuEntry( "Trojkaty z tablicami wierzcholkow", TRIANGLES_VA );
    #endif
   
    // 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 );
    #ifdef WIN32
   
    glutAddSubMenu( "Test", MenuTest );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującego menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // utworzenie list wyświetlania
    GenerateDisplayLists();
   
    // funkcja bezczynności
    glutIdleFunc( DisplayScene );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Drugi i ostatni program przykładowy (plik indeksowe_tablice_wierzcholkow.cpp) korzysta z indeksowych tablic wierzchołków. Dane tablic wierzchołków zebrane są w dwóch tabelach. Pierwsza tabela zawiera współrzędne wektorów normalnych i wierzchołków rysowanego obiektu - dwudziestościanu, natomiast druga tabela zawiera składowe kolorów wierzchołków dwudziestościanu. Stąd przy definiowaniu danych użyto zarówno funkcji glInterleavedArrays jak i glColorPointer.
Dodatkowo program sprawdza czy implementacja biblioteki OpenGL obsługuje rozszerzenie EXT draw range elements. Jeżeli tak to do rysowania dwudziestościanu używana jest funkcja glDrawRangeElements. W przeciwnym wypadku obiekt rysuje funkcja glDrawElements.
Efekt działania programu z przyznajmy szczerze zupełnie przypadkowo dobranymi kolorami wierzchołków przedstawia rysunek 3.

Plik indeksowe_tablice_wierzcholkow.cpp

Rysunek 3. Program Indeksowe tablice wierzchołków
Rysunek 3. Program Indeksowe tablice wierzchołków
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <GL/glext.h>
#ifndef WIN32
#define GLX_GLXEXT_LEGACY
#include <GL/glx.h>
#define wglGetProcAddress glXGetProcAddressARB
#endif
#include <stdlib.h>
#include <stdio.h>

// wskaźnik na funkcję glDrawRangeElementsEXT

PFNGLDRAWRANGEELEMENTSEXTPROC glDrawRangeElementsEXT = NULL;

// 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;

// usunięcie definicji makr near i far

#ifdef near
#undef near
#endif
#ifdef far
#undef far
#endif

// 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 = 2.0;

// współrzędne wektorów normalnych i wierzchołków dwudziestościanu

GLfloat normal_vertex[ 12 * 6 ] =
{
    0.000000, 0.848013, 0.529976, 0.000, 0.667, 0.500, // v0
    0.000000, 0.848013, - 0.529976, 0.000, 0.667, - 0.500, // v1
    0.000000, - 0.848013, - 0.529976, 0.000, - 0.667, - 0.500, // v2
    0.000000, - 0.848013, 0.529976, 0.000, - 0.667, 0.500, // v3
    0.848013, 0.529976, 0.000000, 0.667, 0.500, 0.000, // v4
    0.848013, - 0.529976, 0.000000, 0.667, - 0.500, 0.000, // v5
    - 0.848013, - 0.529976, 0.000000, - 0.667, - 0.500, 0.000, // v6
    - 0.848013, 0.529976, 0.000000, - 0.667, 0.500, 0.000, // v7
    0.529976, 0.000000, 0.848013, 0.500, 0.000, 0.667, // v8
    - 0.529976, 0.000000, 0.848013, - 0.500, 0.000, 0.667, // v9
    - 0.529976, 0.000000, - 0.848013, - 0.500, 0.000, - 0.667, // v10
    0.529976, 0.000000, - 0.848013, 0.500, 0.000, - 0.667 // v11
};

// składowe kolorów wierzchołków dwudziestościanu

GLfloat color[ 12 * 3 ] =
{
    1.0, 0.0, 0.0, // v0
    1.0, 0.0, 1.0, // v1
    1.0, 0.0, 0.0, // v2
    1.0, 0.0, 1.0, // v3
    0.0, 1.0, 0.0, // v4
    1.0, 1.0, 0.0, // v5
    0.0, 1.0, 0.0, // v6
    1.0, 1.0, 0.0, // v7
    0.0, 0.0, 1.0, // v8
    0.0, 1.0, 1.0, // v9
    0.0, 0.0, 1.0, // v10
    0.0, 1.0, 1.0 // v11
};

// opis ścian dwudziestościanu

GLubyte triangles[ 20 * 3 ] =
{
    2, 10, 11,
    1, 11, 10,
    1, 10, 7,
    1, 4, 11,
    0, 1, 7,
    0, 4, 1,
    0, 9, 8,
    3, 8, 9,
    0, 7, 9,
    0, 8, 4,
    3, 9, 6,
    3, 5, 8,
    2, 3, 6,
    2, 5, 3,
    2, 6, 10,
    2, 11, 5,
    6, 7, 10,
    6, 9, 7,
    4, 5, 11,
    4, 8, 5
};

// 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 );
   
    // utworzenie danych o wektorach normalnych i współrzędnych wierzchołków
    glInterleavedArrays( GL_N3F_V3F, 0, normal_vertex );
   
    // włączenie tablicy z danymi o kolorach wierzchołków
    glEnableClientState( GL_COLOR_ARRAY );
    glColorPointer( 3, GL_FLOAT, 0, color );
   
    // narysowanie tablic wierzchołków - wybór funkcji zależy
    // od dostępności rozszerzenia EXT_draw_range_elements
    if( glDrawRangeElementsEXT == NULL )
         glDrawElements( GL_TRIANGLES, 20 * 3, GL_UNSIGNED_BYTE, triangles );
    else
         glDrawRangeElementsEXT( GL_TRIANGLES, 0, 11, 20 * 3, GL_UNSIGNED_BYTE, triangles );
   
    // 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 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 )
    {
        // 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 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 );
    }
}

// sprawdzenie i przygotowanie obsługi wybranych rozszerzeń

void ExtensionSetup()
{
    // pobranie numeru wersji biblioteki OpenGL
    const char * version =( char * ) glGetString( GL_VERSION );
   
    // odczyt wersji OpenGL
    int major = 0, minor = 0;
    if( sscanf( version, "%d.%d", & major, & minor ) != 2 )
    {
        #ifdef WIN32
        printf( "Błędny format wersji OpenGL\n" );
        #else
       
        printf( "Bledny format wersji OpenGL\n" );
        #endif
       
        exit( 0 );
    }
   
    // sprawdzenie czy jest co najmniej wersja 1.2
    if( major > 1 || minor >= 2 )
    {
        // pobranie wskaźników wybranych funkcji OpenGL 1.2
        glDrawRangeElementsEXT =
        ( PFNGLDRAWRANGEELEMENTSEXTPROC ) wglGetProcAddress( "glDrawRangeElements" );
    }
    else
   
    // sprawdzenie czy jest obsługiwane rozszerzenie EXT_draw_range_elements
    if( glutExtensionSupported( "GL_EXT_draw_range_elements" ) )
    {
        // pobranie wskaźników wybranych funkcji rozszerzenia EXT_draw_range_elements
        glDrawRangeElementsEXT =
        ( PFNGLDRAWRANGEELEMENTSEXTPROC ) wglGetProcAddress( "glDrawRangeElementsEXT" );
    }
    else
    {
        printf( "Brak rozszerzenia EXT_draw_range_elements!\n" );
        glDrawRangeElementsEXT = NULL;
    }
}

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( "Indeksowe tablice wierzchołków" );
    #else
   
    glutCreateWindow( "Indeksowe tablice wierzcholkow" );
    #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 );
   
    // sprawdzenie i przygotowanie obsługi wybranych rozszerzeń
    ExtensionSetup();
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokument Następny dokument
Sprzężenie zwrotne Mgła