Przy rysowaniu wielokątów biblioteka OpenGL oferuje znacznie większe możliwości niż przy rysowaniu pozostałych prymitywów gra?cznych. Zamieszczone poniżej informacje dotyczą wielokątów de?niowanych pomiędzy wywołaniami funkcji glBegin i glEnd (stałe: GL_TRIANGLE_STRIP, GL_TRIANGLES, GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP i GL_POLYGON) oraz częściowo prostokątów rysowanych za pomocą funkcji z grupy glRect.
Strony wielokąta
Podstawową zadaniem przy rysowaniu wielokątów jest określenie stron wielokąta. Biblioteka OpenGL operuje pojęciem przedniej i tylnej strony wielokąta. Domyślnie przednią stroną wielokąta jest ta, której wierzchołki uporządkowane są w kierunku przeciwnym do ruchu wskazówek zegara. Zmianę sposobu określania stron wielokątów umożliwia funkcja:
void glFrontFace( GLenum mode )
Parametr mode przyjmuje jedną z dwóch wartości:
Określenie stron wielokątów umożliwia optymalizację procesu rysowania sceny 3D. W wielu bowiem przypadkach ?gury tworzone z wielokątów są obiektami zamkniętymi, a ich wewnętrzna część nie jest widoczna w programie. W takich sytuacjach OpenGL umożliwia wybór, która strona wielokąta nie ma być rysowana. W tym celu należy wywołać funkcję:
void glCullFace( GLenum mode )
której parametr mode przyjmuje jedną z wartości:
Zauważmy, że użycie parametru GL_FRONT_AND_BACK spowoduje wyłączenie rysowania wielokątów. Domyślnie opcja wyboru renderowanej strony wielokątów jest wyłączona, czyli rysowane są obie strony wielokątów. Po włączeniu tej opcji (funkcja glEnable z parametrem GL_CULL_FACE) domyślnie rysowana jest tylko przednia strona wielokątów.
Tryby rysowania
Biblioteka OpenGL umożliwia także wybór różnego sposobu rysowania przednich i tylnych stron wielokątów. Realizuje to funkcja:
void glPolygonMode( GLenum face, GLenum mode )
Parametr face określa stronę wielokąta:
Parametr mode określa metodę rysowania wybranej strony wielokąta:
Domyślne ustawione jest rysowanie wielokątów wypełnionych po obu stronach. Warto zauważyć, że przy rysowaniu wielokątów można stosować także ustawienia dotyczące wielkości punktów, szerokości linii oraz wzorów linii.
Wypełnianie wielokątów wzorem
Podobnie jak w przypadku linii także wielokąty można rysować przy użyciu wzoru. Wzór ma postać mapy bitowej o rozmiarach 32 × 32 pikseli i jest reprezentowany przez tablicę 128 liczb typu GLubyte. Dane mapy bitowej zapisywane są wierszami w odwrotnej kolejności tj. od wiersza dolnego do górnego. Zmianę wzoru wypełnienia wielokąta umożliwia funkcja:
void glPolygonStipple( const GLubyte * mask )
Domyślnie wypełnianie wielokątów wzorem jest wyłączone, stąd w celu zastosowania tej możliwości należy wywołać funkcję glEnable z parametrem GL_POLYGON_STIPPLE.
Ukrywanie krawędzi wielokąta
Domyślnie OpenGL rysuje wszystkie krawędzie wielokątów. Jednak przy wyświetlaniu wielokątów złożonych z wielu ?gur podstawowych (np. trójkątów) w trybie GL_LINE przydatna jest możliwość ukrywanie wybranych krawędzi wielokątów. Wybór czy aktualna krawędź ma być rysowana czy też nie umożliwiają funkcje:
void glEdgeFlag( GLboolean flag )
void glEdgeFlagv( const GLboolean * flag )
Parametr flag określa czy bieżąca krawędź wielokąta ma być rysowana (GL_TRUE) czy nie (GL_FALSE). Ustawienie wskaźnika rysowania krawędzi obowiązuje aż do jego zmiany. Jak się już Czytelnik domyślił stałe GL_TRUE i GL_FALSE to zde?niowane w bibliotece OpenGL odpowiedniki true i false z języka C++.
Ukrywanie krawędzi nie działa w przypadku prostokątów rysowanych przy użyciu funkcji z grupy glRect.
Podział wielokątów
Funkcje biblioteki OpenGL nie umożliwiają np. rysowania wielokątów z dziurami. W takim przypadku przydatne okazują się mechanizmy zawarte w bibliotece GLU umożliwiające podział (kafelkowanie) wielokątów.
Do kafelkowania używana jest struktura (w przypadku języka C++ klasa) GLUtesselator. Można także używać alternatywnych nazw klas/struktur: GLUtesselatorObj i GLUtriangulatorObj Obiekt podziału tworzy wywołanie funkcji gluNewTess:
GLUtesselator * gluNewTess( void )
która zwraca wskaźnik do struktury (klasy) GLUtesselator, niezbędny w wywołaniach pozostałych funkcji obsługujących kafelkowanie wielokątów. Po zakończeniu operacji na danym obiekcie podziału należy zwolnić przydzieloną mu pamięć wywołując funkcję:
void gluDeleteTess( GLUtesselator * tess )
Defnicja wielokąta
Defnicję wielokąta rozpoczyna wywołanie funkcji:
void gluTessBeginPolygon( GLUtesselator * tess,
GLvoid * polygon_data )
gdzie parametr polygon data zawiera dowolne dane użytkownika i w szczególności może przyjąć wartość NULL. Dane te przekazywane są do opisanych dalej funkcji zwrotnych. Po zakończeniu de?niowania wielokąta trzeba wywołać funkcję:
void gluTessEndPolygon( GLUtesselator * tess )
Wewnątrz wielokąta umieszcza się de?nicje jednego lub wielu konturów.
Defnicja każdego konturu zawiera się pomiędzy wywołaniami funkcji:
void gluTessBeginContour( GLUtesselator * tess )
void gluTessEndContour( GLUtesselator * tess )
Kontury wielokąta opisuje się za pomocą współrzędnych ich wierzchołków, przy czym pierwszy wierzchołek jest automatycznie łączony z ostatnim.
Wierzchołek de?niuje się przy użyciu funkcji:
void gluTessVertex( GLUtesselator * tess,
GLdouble coords[ 3 ],
GLvoid * vertex_data )
gdzie parametr coords określa współrzędne (x, y, z) wierzchołka, a w parametrze vertex data przekazywane są dodatkowe dane opisujące wierzchołek. Może to być np. jego kolor c zy współrzędne tekstury. Dane te przekazywane są jako parametr opisanych dalej funkcji zwrotnych GLU_VERTEX, GLU_TESS_VERTEX i GLU_TESS_VERTEX_DATA.
W celu zachowania wstecznej zgodności GLU w wersjach 1.2 i 1.3 udostępnia także pochodzący z wcześniejszych wersji biblioteki interfejs do de?niowania wielokątów. Składają się na niego trzy poniższe funkcje:
void gluBeginPolygon( GLUtesselator * tess )
void gluNextContour( GLUtesselator * tess,
GLenum type )
void gluEndPolygon( GLUtesselator * tess )
Funkcja gluBeginPolygon tworzy nowy wielokąt i jednocześnie stanowi początek de?nicji konturu. Stanowi więc odpowiednik wywołania kolejno funkcji gluTessBeginPolygon i gluTessBeginContour. Druga funkcja tworzy nowy kontur i odpowiada wywołaniu gluTessEndContour i gluTessBeginContour. Wartość parametru type jest ignorowana, choć dostępne są stałe, które określały jego dopuszczalne wartości: GLU_CW, GLU_CCW, GLU_INTERIOR, GLU_EXTERIOR i GLU_UNKNOWN.
Ostatnia funkcja gluEndPolygon jednocześnie kończy de?nicję konturu i całego wielokąta. Jest to więc odpowiednik wywołania funkcji gluTessEndContour i gluTessEndPolygon.
Specy?kacja biblioteki GLU określa trzy powyższe funkcje jako przestarzałe i zaleca stosowanie wyłącznie nowego interfejsu obsługi wielokątów i konturów.
Funkcje zwrotne
Po utworzeniu obiektu podziału trzeba zde?niować funkcje zwrotne używane podczas różnych etapów kafelkowania wielokątów. Dołączenie funkcji zwrotnej realizuje funkcja:
void gluTessCallback( GLUtesselator * tess,
GLenum which,
void( * fn )() )
której parametr which określa rodzaj wykonywanej czynności lub rodzaj generowanych danych podczas kafelkowania wielokąta, a odpowiednia funkcja zwrotna wskazywana jest w parametrze fn.
Funkcje zwrotne występują w dwóch wersjach. Pierwsza z nich zwraca tylko dane związane z charakterem swojego działania (np. współrzędne wierzchołków wielokąta). Wersje alternatywne zwracają dodatkowo parametr polygon data zawierający dane użytkownika przekazane w parametrze polygon data funkcji gluTessBeginPolygon.
Funkcje zwrotne renderera obiektów podziału określone są następującymi stałymi:
Tabela 1: Zestawienie kodów błędów obiektów podziału wielokątów
Wszystkie funkcje zwrotne domyślnie są niezde?niowane. W przypadku konieczności deaktywacji wybranej funkcji zwrotnej jako parametr fn funkcji gluTessCallback trzeba podać wartość NULL.
Właściwości kafelkowania
Modyfkację właściwości podziału wielokątów umożliwia funkcja:
void gluTessProperty( GLUtesselator * tess,
GLenum which,
GLdouble data )
której parametr which określa rodzaj mody?kowanej właściwości, a parametr data zawiera nową wartość właściwości. Parametr which może przyjąć jedną z trzech wartości: GLU_TESS_WINDING_RULE, GLU_TESS_BOUNDARY_ONLY i GLU_TESS_TOLERANCE.
Pierwsza właściwość GLU_TESS_WINDING_RULE określa, która z części wielokąta (który kontur) stanowi część wewnętrzną wielokąta. W tym celu biblioteka GLU określa dla każdego konturu tzw. liczby nawijania (ang.
winding numbers). Wartość 1 otrzymuje każdy kontur złożony z krawędzi o kierunku odwrotnym do ruchu wskazówek zegara. Wartość -1 przyjmuje kontur złożony z krawędzi o kierunku zgodnym z ruchem wskazówek zegara. Wartość liczby nawijania konturu wewnętrznego jest sumą wartości liczby tego konturu i wszystkich konturów zewnętrznych. Liczby nawijania dla dwóch przykładowych konturów przedstawia rysunek 1.
Możliwe są następujące wartości właściwości GLU_TESS_WINDING_RULE:
wspólnej dwóch konturów (prawidłowo działa tylko dla wielokąta z dwoma konturami).
Właściwość GLU_TESS_BOUNDARY_ONLY przyjmuje wartości logiczne GL_TRUE i GL_FALSE. Wartość GL_TRUE oznacza, że renderowane są wyłącznie krawędzie wielokąta. Tylko w takim przypadku wartość parametru type funkcji zwrotnych GLU_BEGIN, GLU_TESS_BEGIN i GLU_TESS_BEGIN_DATA wynosi GL_LINE_LOOP. Domyślna wartość GL_TRUE oznacza, że renderowany jest wypełniony wielokąt.
Trzecia i ostatnia właściwość GLU_TESS_TOLERANCE określa tolerancję używaną przy podziale wielokąta na trójkąty w przypadku występowania blisko położonych wierzchołków. Właściwość ta jest opcjonalna i może być pominięta przez implementację biblioteki GLU. Domyślnie wartość tolerancji wynosi 0.
Pobieranie wartości wybranych właściwości kafelkowania umożliwia funkcja:
void gluGetTessProperty( GLUtesselator * tess,
GLenum which,
GLdouble * data )
której parametr which przyjmuje takie same wartości jak analogiczny parametr funkcji gluTessProperty.
Ostatnią funkcją biblioteki GLU związaną z kafelkowaniem wielokątów jest funkcja:
void gluTessNormal( GLUtesselator * tess,
GLdouble valueX,
GLdouble valueY, GLdouble valueZ )
która w parametrach valueX, valueY i valueZ zawiera współrzędne wektora normalnego dla każdego z trójkątów generowanych podczas podziału wielokąta. Funkcja jest oczywiście szczególnie użyteczna wówczas, gdy cały wielokąt znajduje się w jednej płaszczyźnie. Domyślnie współrzędne wektora normalnego wynoszą (0, 0, 0).
Programy przykładowe
Pierwszy przykładowy program (plik wielokaty.cpp) prezentuje wszystkie opisane wyżej możliwości związane z rysowaniem wielokątów. Wybór rodzaju wielokąta oraz opcji jego rysowania tradycyjnie umożliwia menu podręczne.
W przykładowym programie pobieramy i wyświetlamy wartości wybranych zmiennych maszyny stanów OpenGL. Odczyt parametru określanego wywołaniem funkcji glEnable/glDisable umożliwia funkcja:
GLboolean glIsEnabled( GLenum cap )
która zwraca odpowiednio wartość GL_TRUE lub GL_FALSE. Parametr cap przyjmuje taką samą wartość jak parametr funkcji glEnable/glDisable.
Poza powyższą funkcją i poznanymi już funkcjami glGetBooleanv, glGetDoublev, glGetFloatv i glGetIntegerv do pobierania wartości niektórych zmiennych maszyny stanów OpenGL służą specjalne funkcje. Przykładem jest niezastosowana w poniższym programie funkcja pobierająca wzór wypełnienia wielokąta:
void glGetPolygonStipple( GLubyte * mask )
W przykładowym programie pobierane są i wyświetlane na ekranie m.in.
następujące zmienne maszyny stanów OpenGL:
Przykładowe efekty działania programu przestawiają rysunki: 2, 3, 4 i 5.
Plik podzial_wielokatow.cpp
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
enum
{
CLEAR_VERTEX,
EXIT
};
#define MAX_VERTEX 2000
GLdouble vertex[ MAX_VERTEX ][ 3 ];
int vertex_count = 0;
GLenum primitive = 0;
GLdouble winding_rule = GLU_TESS_WINDING_ODD;
GLdouble boundary_only = GL_FALSE;
#ifndef CALLBACK
#define CALLBACK
#endif
void CALLBACK beginCallback( GLenum type )
{
glBegin( type );
primitive = type;
}
void CALLBACK errorCallback( GLenum errorCode )
{
printf( "Błąd kafelkowania: %s\n", gluErrorString( errorCode ) );
exit( 0 );
}
void CALLBACK combineCallback( GLdouble coords[ 3 ],
GLdouble * vertex_data[ 4 ],
GLfloat weight[ 4 ],
GLdouble ** dataOut )
{
GLdouble * new_vertex =( GLdouble * ) malloc( 3 * sizeof( GLdouble ) );
new_vertex[ 0 ] = coords[ 0 ];
new_vertex[ 1 ] = coords[ 1 ];
new_vertex[ 2 ] = coords[ 2 ];
* dataOut = new_vertex;
}
void DrawString( int x, int y, char * string )
{
glRasterPos2i( x, y );
int len = strlen( string );
for( int i = 0; i < len; i++ )
glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
}
void Display()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3f( 0.0, 1.0, 0.0 );
GLUtesselator * tess = gluNewTess();
gluTessCallback( tess, GLU_TESS_VERTEX,( _GLUfuncptr ) glVertex3dv );
gluTessCallback( tess, GLU_TESS_BEGIN,( _GLUfuncptr ) beginCallback );
gluTessCallback( tess, GLU_TESS_END,( _GLUfuncptr ) glEnd );
gluTessCallback( tess, GLU_TESS_ERROR,( _GLUfuncptr ) errorCallback );
gluTessCallback( tess, GLU_TESS_COMBINE,( _GLUfuncptr ) combineCallback );
gluTessProperty( tess, GLU_TESS_WINDING_RULE, winding_rule );
gluTessProperty( tess, GLU_TESS_BOUNDARY_ONLY, boundary_only );
gluTessBeginPolygon( tess, NULL );
gluTessBeginContour( tess );
for( int i = 0; i < vertex_count; i++ )
gluTessVertex( tess, vertex[ i ], vertex[ i ] );
gluTessEndContour( tess );
gluTessEndPolygon( tess );
gluDeleteTess( tess );
glColor3f( 0.0, 0.0, 0.0 );
if( primitive != GL_LINE_LOOP )
{
glBegin( GL_LINE_LOOP );
for( int i = 0; i < vertex_count; i++ )
glVertex3dv( vertex[ i ] );
glEnd();
}
for( int i = 0; i < vertex_count; i++ )
{
glRasterPos2i( vertex[ i ][ 0 ] + 2, vertex[ i ][ 1 ] + 2 );
char str[ 10 ];
sprintf( str, "%i", i + 1 );
int len = strlen( str );
for( int j = 0; j < len; j++ )
glutBitmapCharacter( GLUT_BITMAP_9_BY_15, str[ j ] );
}
if( primitive == GL_TRIANGLE_FAN )
DrawString( 2, 2, "GLU_TESS_BEGIN = GL_TRIANGLE_FAN" );
else
if( primitive == GL_TRIANGLE_STRIP )
DrawString( 2, 2, "GLU_TESS_BEGIN = GL_TRIANGLE_STRIP" );
else
if( primitive == GL_TRIANGLES )
DrawString( 2, 2, "GLU_TESS_BEGIN = GL_TRIANGLES" );
else
if( primitive == GL_LINE_LOOP )
DrawString( 2, 2, "GLU_TESS_BEGIN = GL_LINE_LOOP" );
if( boundary_only == GL_TRUE )
DrawString( 2, 16, "GLU_TESS_BOUNDARY_ONLY = GL_TRUE" );
else
DrawString( 2, 16, "GLU_TESS_BOUNDARY_ONLY = GL_FALSE" );
switch(( int ) winding_rule )
{
case GLU_TESS_WINDING_ODD:
DrawString( 2, 30, "GLU_TESS_WINDING_RULE = GLU_TESS_WINDING_ODD" );
break;
case GLU_TESS_WINDING_NONZERO:
DrawString( 2, 30, "GLU_TESS_WINDING_RULE = GLU_TESS_WINDING_NONZERO" );
break;
case GLU_TESS_WINDING_POSITIVE:
DrawString( 2, 30, "GLU_TESS_WINDING_RULE = GLU_TESS_WINDING_POSITIVE" );
break;
case GLU_TESS_WINDING_NEGATIVE:
DrawString( 2, 30, "GLU_TESS_WINDING_RULE = GLU_TESS_WINDING_NEGATIVE" );
break;
case GLU_TESS_WINDING_ABS_GEQ_TWO:
DrawString( 2, 30, "GLU_TESS_WINDING_RULE = GLU_TESS_WINDING_ABS_GEQ_TWO" );
break;
}
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D( 0, width, 0, height );
Display();
}
void MouseButton( int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN && vertex_count < MAX_VERTEX )
{
vertex[ vertex_count ][ 0 ] = x;
vertex[ vertex_count ][ 1 ] = glutGet( GLUT_WINDOW_HEIGHT ) - y;
vertex[ vertex_count ][ 2 ] = 0.0;
vertex_count++;
Display();
}
}
void Menu( int value )
{
switch( value )
{
case GLU_TESS_WINDING_ODD:
case GLU_TESS_WINDING_NONZERO:
case GLU_TESS_WINDING_POSITIVE:
case GLU_TESS_WINDING_NEGATIVE:
case GLU_TESS_WINDING_ABS_GEQ_TWO:
winding_rule = value;
Display();
break;
case GLU_TESS_BOUNDARY_ONLY:
boundary_only = !boundary_only;
Display();
break;
case CLEAR_VERTEX:
vertex_count = 0;
Display();
break;
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
glutInitWindowSize( 500, 500 );
#ifdef WIN32
glutCreateWindow( "Podział wielokątów" );
#else
glutCreateWindow( "Podzial wielokatow" );
#endif
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
glutMouseFunc( MouseButton );
int MenuWindingRule = glutCreateMenu( Menu );
glutAddMenuEntry( "GLU_TESS_WINDING_ODD", GLU_TESS_WINDING_ODD );
glutAddMenuEntry( "GLU_TESS_WINDING_NONZERO", GLU_TESS_WINDING_NONZERO );
glutAddMenuEntry( "GLU_TESS_WINDING_POSITIVE", GLU_TESS_WINDING_POSITIVE );
glutAddMenuEntry( "GLU_TESS_WINDING_NEGATIVE", GLU_TESS_WINDING_NEGATIVE );
glutAddMenuEntry( "GLU_TESS_WINDING_ABS_GEQ_TWO", GLU_TESS_WINDING_ABS_GEQ_TWO );
glutCreateMenu( Menu );
glutAddSubMenu( "GLU_TESS_WINDING_RULE", MenuWindingRule );
glutAddMenuEntry( "GLU_TESS_BOUNDARY_ONLY: GL_TRUE/GL_FALSE", GLU_TESS_BOUNDARY_ONLY );
#ifdef WIN32
glutAddMenuEntry( "Usuń wierzchołki", CLEAR_VERTEX );
glutAddMenuEntry( "Wyjście", EXIT );
#else
glutAddMenuEntry( "Usun wierzcholki", CLEAR_VERTEX );
glutAddMenuEntry( "Wyjscie", EXIT );
#endif
glutAttachMenu( GLUT_RIGHT_BUTTON );
glutMainLoop();
return 0;
}
Plik wielokaty.cpp
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <vector>
GLubyte chequers4x4[] =
{
0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F,
0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0,
0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F,
0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0,
0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F,
0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0,
0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F,
0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0
};
GLubyte chequers8x8[] =
{
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00,
};
enum
{
RECTANGLES = GL_POLYGON + 100,
CLEAR_VERTEX,
MODE_FRONT_FACE_POINT,
MODE_FRONT_FACE_LINE,
MODE_FRONT_FACE_FILL,
MODE_BACK_FACE_POINT,
MODE_BACK_FACE_LINE,
MODE_BACK_FACE_FILL,
CHEQUERS_8X8,
CHEQUERS_4X4,
CULL_FACE_ON_OFF,
POLYGON_STIPPLE_ON_OFF,
EDGE_FLAG_ON_OFF,
EXIT
};
GLenum polygon = GL_TRIANGLES;
GLenum cull_face = GL_BACK;
GLenum front_face = GL_CCW;
GLenum polygon_mode_front_face = GL_FILL;
GLenum polygon_mode_back_face = GL_FILL;
GLubyte * polygon_stipple = chequers8x8;
GLboolean cull_face_on_off = GL_TRUE;
GLboolean polygon_stipple_on_off = GL_TRUE;
GLboolean edge_flag = GL_TRUE;
std::vector < GLint > vertex_x;
std::vector < GLint > vertex_y;
std::vector < GLboolean > edge_flags;
void DrawString( int x, int y, char * string )
{
glRasterPos2i( x, y );
int len = strlen( string );
for( int i = 0; i < len; i++ )
glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
}
void DrawParameters()
{
char string[ 100 ];
GLfloat f;
GLboolean b;
GLint itab[ 2 ], i;
glColor3f( 0.0, 0.0, 0.0 );
if( glIsEnabled( GL_CULL_FACE ) == GL_TRUE )
strcpy( string, "GL_CULL_FACE: true" );
else
strcpy( string, "GL_CULL_FACE: false" );
DrawString( 0, 1, string );
glGetIntegerv( GL_CULL_FACE_MODE, & i );
if( i == GL_BACK )
strcpy( string, "GL_CULL_FACE_MODE: GL_BACK" );
else
if( i == GL_FRONT )
strcpy( string, "GL_CULL_FACE_MODE: GL_FRONT" );
else
if( i == GL_FRONT_AND_BACK )
strcpy( string, "GL_CULL_FACE_MODE: GL_FRONT_AND_BACK" );
DrawString( 0, 16, string );
glGetIntegerv( GL_FRONT_FACE, & i );
if( i == GL_CCW )
strcpy( string, "GL_FRONT_FACE: GL_CCW" );
else
if( i == GL_CW )
strcpy( string, "GL_FRONT_FACE: GL_CW" );
DrawString( 0, 31, string );
glGetIntegerv( GL_POLYGON_MODE, itab );
strcpy( string, "GL_POLYGON_MODE: " );
if( itab[ 0 ] == GL_POINT )
strcat( string, "GL_POINT (front), " );
else
if( itab[ 0 ] == GL_LINE )
strcat( string, "GL_LINE (front), " );
else
if( itab[ 0 ] == GL_FILL )
strcat( string, "GL_FILL (front), " );
if( itab[ 1 ] == GL_POINT )
strcat( string, "GL_POINT (back)" );
else
if( itab[ 1 ] == GL_LINE )
strcat( string, "GL_LINE (back)" );
else
if( itab[ 1 ] == GL_FILL )
strcat( string, "GL_FILL (back)" );
DrawString( 0, 46, string );
if( glIsEnabled( GL_POLYGON_STIPPLE ) == GL_TRUE )
strcpy( string, "GL_POLYGON_STIPPLE: true" );
else
strcpy( string, "GL_POLYGON_STIPPLE: false" );
DrawString( 0, 61, string );
glGetBooleanv( GL_EDGE_FLAG, & b );
if( b == GL_TRUE )
strcpy( string, "GL_EDGE_FLAG: true" );
else
strcpy( string, "GL_EDGE_FLAG: false" );
DrawString( 0, 76, string );
glGetFloatv( GL_POINT_SIZE, & f );
sprintf( string, "GL_POINT_SIZE: %f", f );
DrawString( 0, 91, string );
glGetFloatv( GL_LINE_WIDTH, & f );
sprintf( string, "GL_LINE_WIDTH: %f", f );
DrawString( 0, 106, string );
}
void Display()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3f( 1.0, 0.0, 0.0 );
glFrontFace( front_face );
glPolygonMode( GL_FRONT, polygon_mode_front_face );
glPolygonMode( GL_BACK, polygon_mode_back_face );
if( polygon_stipple_on_off == GL_TRUE )
glEnable( GL_POLYGON_STIPPLE );
if( cull_face_on_off == GL_TRUE )
glEnable( GL_CULL_FACE );
glPolygonStipple( polygon_stipple );
glCullFace( cull_face );
glPointSize( 3.0 );
glLineWidth( 3.0 );
if( polygon == RECTANGLES )
for( unsigned int i = 0; i + 1 < vertex_x.size(); i += 2 )
glRecti( vertex_x[ i ], vertex_y[ i ], vertex_x[ i + 1 ], vertex_y[ i + 1 ] );
else
glBegin( polygon );
for( unsigned int i = 0; i < vertex_x.size(); i++ )
{
glEdgeFlag( edge_flags[ i ] );
glVertex2i( vertex_x[ i ], vertex_y[ i ] );
}
glEnd();
DrawParameters();
glDisable( GL_POLYGON_STIPPLE );
glDisable( GL_CULL_FACE );
glColor3f( 0.0, 0.0, 0.0 );
for( unsigned int i = 0; i < vertex_x.size(); i++ )
{
glRasterPos2i( vertex_x[ i ] + 2, vertex_y[ i ] + 2 );
char str[ 10 ];
sprintf( str, "%i", i + 1 );
int len = strlen( str );
for( int i = 0; i < len; i++ )
glutBitmapCharacter( GLUT_BITMAP_9_BY_15, str[ i ] );
}
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D( 0, width, 0, height );
Display();
}
void MouseButton( int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN )
{
vertex_x.insert( vertex_x.end(), x );
vertex_y.insert( vertex_y.end(), glutGet( GLUT_WINDOW_HEIGHT ) - y );
edge_flags.insert( edge_flags.end(), edge_flag );
Display();
}
}
void Menu( int value )
{
switch( value )
{
case GL_TRIANGLES:
polygon = GL_TRIANGLES;
#ifdef WIN32
glutSetWindowTitle( "Wielokąty - trójkąty (GL_TRIANGLES)" );
#else
glutSetWindowTitle( "Wielokaty - trojkaty (GL_TRIANGLES)" );
#endif
Display();
break;
case GL_TRIANGLE_STRIP:
polygon = GL_TRIANGLE_STRIP;
#ifdef WIN32
glutSetWindowTitle( "Wielokąty - wstęga trójkątów (GL_TRIANGLE_STRIP)" );
#else
glutSetWindowTitle( "Wielokaty - wstega trojkatow (GL_TRIANGLE_STRIP)" );
#endif
Display();
break;
case GL_TRIANGLE_FAN:
polygon = GL_TRIANGLE_FAN;
#ifdef WIN32
glutSetWindowTitle( "Wielokąty - wachlarz trójkątów (GL_TRIANGLE_FAN)" );
#else
glutSetWindowTitle( "Wielokaty - wachlarz trojkatow (GL_TRIANGLE_FAN)" );
#endif
Display();
break;
case GL_QUADS:
polygon = GL_QUADS;
#ifdef WIN32
glutSetWindowTitle( "Wielokąty - czworokąty (GL_QUADS)" );
#else
glutSetWindowTitle( "Wielokaty - czworokaty (GL_QUADS)" );
#endif
Display();
break;
case GL_QUAD_STRIP:
polygon = GL_QUAD_STRIP;
#ifdef WIN32
glutSetWindowTitle( "Wielokąty - wstęga czworokątów (GL_QUAD_STRIP)" );
#else
glutSetWindowTitle( "Wielokaty - wstega czworokatow (GL_QUAD_STRIP)" );
#endif
Display();
break;
case GL_POLYGON:
polygon = GL_POLYGON;
#ifdef WIN32
glutSetWindowTitle( "Wielokąt (GL_POLYGON)" );
#else
glutSetWindowTitle( "Wielokat (GL_POLYGON)" );
#endif
Display();
break;
case RECTANGLES:
polygon = RECTANGLES;
#ifdef WIN32
glutSetWindowTitle( "Wielokąty - prostokąty" );
#else
glutSetWindowTitle( "Wielokaty - prostokaty" );
#endif
Display();
break;
case CLEAR_VERTEX:
vertex_x.erase( vertex_x.begin(), vertex_x.end() );
vertex_y.erase( vertex_y.begin(), vertex_y.end() );
edge_flags.erase( edge_flags.begin(), edge_flags.end() );
Display();
break;
case GL_CCW:
front_face = GL_CCW;
Display();
break;
case GL_CW:
front_face = GL_CW;
Display();
break;
case GL_FRONT:
cull_face = GL_FRONT;
Display();
break;
case GL_BACK:
cull_face = GL_BACK;
Display();
break;
case GL_FRONT_AND_BACK:
cull_face = GL_FRONT_AND_BACK;
Display();
break;
case MODE_FRONT_FACE_POINT:
polygon_mode_front_face = GL_POINT;
Display();
break;
case MODE_FRONT_FACE_LINE:
polygon_mode_front_face = GL_LINE;
Display();
break;
case MODE_FRONT_FACE_FILL:
polygon_mode_front_face = GL_FILL;
Display();
break;
case MODE_BACK_FACE_POINT:
polygon_mode_back_face = GL_POINT;
Display();
break;
case MODE_BACK_FACE_LINE:
polygon_mode_back_face = GL_LINE;
Display();
break;
case MODE_BACK_FACE_FILL:
polygon_mode_back_face = GL_FILL;
Display();
break;
case CHEQUERS_8X8:
polygon_stipple = chequers8x8;
Display();
break;
case CHEQUERS_4X4:
polygon_stipple = chequers4x4;
Display();
break;
case CULL_FACE_ON_OFF:
cull_face_on_off = !cull_face_on_off;
Display();
break;
case POLYGON_STIPPLE_ON_OFF:
polygon_stipple_on_off = !polygon_stipple_on_off;
Display();
break;
case EDGE_FLAG_ON_OFF:
edge_flag = !edge_flag;
Display();
break;
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
glutInitWindowSize( 550, 500 );
#ifdef WIN32
glutCreateWindow( "Wielokąty - trójkąty (GL_TRIANGLES)" );
#else
glutCreateWindow( "Wielokaty - trojkaty (GL_TRIANGLES)" );
#endif
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
glutMouseFunc( MouseButton );
int MenuPolygons = glutCreateMenu( Menu );
#ifdef WIN32
glutAddMenuEntry( "Trójkąty (GL_TRIANGLES)", GL_TRIANGLES );
glutAddMenuEntry( "Wstęga trójkątów (GL_TRIANGLE_STRIP)", GL_TRIANGLE_STRIP );
glutAddMenuEntry( "Wachlarz trójkątów (GL_TRIANGLE_FAN)", GL_TRIANGLE_FAN );
glutAddMenuEntry( "Czworokąty (GL_QUADS)", GL_QUADS );
glutAddMenuEntry( "Wstęga czworokątów (GL_QUAD_STRIP)", GL_QUAD_STRIP );
glutAddMenuEntry( "Wielokąt (GL_POLYGON)", GL_POLYGON );
glutAddMenuEntry( "Prostokąty", RECTANGLES );
#else
glutAddMenuEntry( "Trojkąty (GL_TRIANGLES)", GL_TRIANGLES );
glutAddMenuEntry( "Wstega trojkatow (GL_TRIANGLE_STRIP)", GL_TRIANGLE_STRIP );
glutAddMenuEntry( "Wachlarz trojkstow (GL_TRIANGLE_FAN)", GL_TRIANGLE_FAN );
glutAddMenuEntry( "Czworokaty (GL_QUADS)", GL_QUADS );
glutAddMenuEntry( "Wstega czworokatow (GL_QUAD_STRIP)", GL_QUAD_STRIP );
glutAddMenuEntry( "Wielokat (GL_POLYGON)", GL_POLYGON );
glutAddMenuEntry( "Prostokaty", RECTANGLES );
#endif
int PolygonFaceOrientation = glutCreateMenu( Menu );
#ifdef WIN32
glutAddMenuEntry( "Odwrotna do ruchu wskazówek zegara (GL_CCW)", GL_CCW );
glutAddMenuEntry( "Zgodna z ruchem wskazówek zegara (GL_CW)", GL_CW );
#else
glutAddMenuEntry( "Odwrotna do ruchu wskazowek zegara (GL_CCW)", GL_CCW );
glutAddMenuEntry( "Zgodna z ruchem wskazowek zegara (GL_CW)", GL_CW );
#endif
int PolygonCullFace = glutCreateMenu( Menu );
glutAddMenuEntry( "Przednia (GL_FRONT)", GL_FRONT );
glutAddMenuEntry( "Tylna (GL_BACK)", GL_BACK );
glutAddMenuEntry( "Przednia i tylna (GL_FRONT_AND_BACK)", GL_FRONT_AND_BACK );
int PolygonModeFrontFace = glutCreateMenu( Menu );
#ifdef WIN32
glutAddMenuEntry( "Wierzchołki (GL_POINT)", MODE_FRONT_FACE_POINT );
glutAddMenuEntry( "Krawędzie (GL_LINE)", MODE_FRONT_FACE_LINE );
glutAddMenuEntry( "Wypełnienie (GL_FILL)", MODE_FRONT_FACE_FILL );
#else
glutAddMenuEntry( "Wierzcholki (GL_POINT)", MODE_FRONT_FACE_POINT );
glutAddMenuEntry( "Krawedzie (GL_LINE)", MODE_FRONT_FACE_LINE );
glutAddMenuEntry( "Wypelnienie (GL_FILL)", MODE_FRONT_FACE_FILL );
#endif
int PolygonModeBackFace = glutCreateMenu( Menu );
#ifdef WIN32
glutAddMenuEntry( "Wierzchołki (GL_POINT)", MODE_BACK_FACE_POINT );
glutAddMenuEntry( "Krawędzie (GL_LINE)", MODE_BACK_FACE_LINE );
glutAddMenuEntry( "Wypełnienie (GL_FILL)", MODE_BACK_FACE_FILL );
#else
glutAddMenuEntry( "Wierzcholki (GL_POINT)", MODE_BACK_FACE_POINT );
glutAddMenuEntry( "Krawedzie (GL_LINE)", MODE_BACK_FACE_LINE );
glutAddMenuEntry( "Wypelnienie (GL_FILL)", MODE_BACK_FACE_FILL );
#endif
int PolygonStipple = glutCreateMenu( Menu );
glutAddMenuEntry( "Szachownica 8x8", CHEQUERS_8X8 );
glutAddMenuEntry( "Szachownica 4x4", CHEQUERS_4X4 );
glutCreateMenu( Menu );
#ifdef WIN32
glutAddSubMenu( "Wielokąty", MenuPolygons );
glutAddSubMenu( "Orientacja stron wielokąta", PolygonFaceOrientation );
glutAddSubMenu( "Nierysowana strona wielokąta", PolygonCullFace );
glutAddSubMenu( "Rysowanie przedniej strony wielokąta", PolygonModeFrontFace );
glutAddSubMenu( "Rysowanie tylnej strony wielokąta", PolygonModeBackFace );
glutAddSubMenu( "Wzór wypełnienia wielokąta", PolygonStipple );
glutAddMenuEntry( "Nierysowanie stron wielokąta: włącz/wyłącz", CULL_FACE_ON_OFF );
glutAddMenuEntry( "Wypełnianie wielokąta wzorem: włącz/wyłącz", POLYGON_STIPPLE_ON_OFF );
glutAddMenuEntry( "Ukrywanie krawędzi włącz/wyłącz", EDGE_FLAG_ON_OFF );
glutAddMenuEntry( "Usuń wierzchołki", CLEAR_VERTEX );
glutAddMenuEntry( "Wyjście", EXIT );
#else
glutAddSubMenu( "Wielokaty", MenuPolygons );
glutAddSubMenu( "Orientacja stron wielokata", PolygonFaceOrientation );
glutAddSubMenu( "Nierysowana strona wielokata", PolygonCullFace );
glutAddSubMenu( "Rysowanie przedniej strony wielokata", PolygonModeFrontFace );
glutAddSubMenu( "Rysowanie tylnej strony wielokata", PolygonModeBackFace );
glutAddSubMenu( "Wzor wypelnienia wielokata", PolygonStipple );
glutAddMenuEntry( "Nierysowanie stron wielokata: włacz/wyłacz", CULL_FACE_ON_OFF );
glutAddMenuEntry( "Wypelnianie wielokata wzorem: włacz/wyłacz", POLYGON_STIPPLE_ON_OFF );
glutAddMenuEntry( "Ukrywanie krawedzi włacz/wyłacz", EDGE_FLAG_ON_OFF );
glutAddMenuEntry( "Usun wierzcholki", CLEAR_VERTEX );
glutAddMenuEntry( "Wyjscie", EXIT );
#endif
glutAttachMenu( GLUT_RIGHT_BUTTON );
glutMainLoop();
return 0;
}