Poza opisanymi i wielokrotnie wykorzystanymi prymitywami graficznymi biblioteka OpenGL dysponuje także alternatywnymi mechanizmami odwzorowania krzywych i powierzchni w postaci krzywych i powierzchni B´eziera.
Krzywe B´eziera zostały opracowane w latach 60 ubiegłego wieku niezależnie przez Pierre B´eziera, francuskiego inżyniera pracującego w firmie Renault oraz Paula de Casteljau pracującego dla konkurencyjnej firmy Citro¨en.
Krzywe B´eziera
Krzywe B´eziera są krzywymi parametrycznymi co oznacza, że każda współrzędna P (x, y, z) punktu krzywej określona jest funkcją parametryczną parametru u z przedziału [0, 1]:
Kształt krzywej określają tzw. punkty kontrolne, których ilość określa jednocześnie stopień krzywej. W praktycznych zastosowaniach wystarczają krzywe trzeciego stopnia, oparte na czterech punktach kontrolnych. Niezależnie od ilości użytych punktów kontrolnych krzywa B´eziera zawsze przechodzi przez pierwszy i ostatni punkt kontrolny.
Przykładowo krzywą B´eziera dla czterech punktów kontrolnych o współrzędnych (x0, y0, z0), (x1 , y1, z1), (x2, y2, z2) i (x3, y3 , z3 ), określają następujące równania:
Tworzenie ewaluatorów
Do generowania krzywych i powierzchni B´eziera biblioteka OpenGL używa tzw. ewaluatorów (ang. evaluators). Ewaluator jednowymiarowy definiują funkcje z grupy glMap1:
void glMap1d( GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble * points )
void glMap1f( GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat * points )
Parametr target określa rodzaj wartości generowanych przez ewaluator. Dopuszczalne są następujące wartości tego parametru:
Współrzędne tekstur generowane są wyłącznie dla pierwszej jednostki teksturującej (
GL_TEXTURE0). Jeżeli aktywną jednostką teksturującą (
GL_ACTIVE_TEXTURE) nie jest
GL_TEXTURE0 zostanie wygenerowany błąd
GL_INVALID_OPERATION.
Parametry u1 i u2 określają przedział wartości parametru u funkcji parametrycznej P (u). Typowo wykorzystywany jest cały przedział wartości parametru u czyli [0, 1].
Parametr stride określa ilość wartości zmiennoprzecinkowych (odpowiednio GLfloat lub GLdouble) zawartych pomiędzy danymi poszczególnych punktów kontrolnych. Umożliwia to umieszczenie w tablicy points innych danych poza współrzędnymi punktów kontrolnych.
Kolejny parametr order określa ilość punktów kontrolnych krzywej zawartych w tablicy points. Maksymalna obsługiwana ilość punktów kontrolnych zależy od implementacji biblioteki OpenGL, ale nie może być mniejsza niż 8. Wielkość tę zawiera zmienna stanu
GL_MAX_EVAL_ORDER.
Jak łatwo zauważyć ewaluator pozwala na wygenerowanie czterech rodzajów wartości. Są to współrzędne punktów krzywej, składowe (lub indeksy) kolorów, współrzędne wektorów normalnych oraz współrzędne tekstury. Przed wywołaniem funkcji ewaluatora trzeba uaktywnić wybrany ewaluator używając funkcji glEnable z parametrem odpowiadającym parametrowi target funkcji z grupy glMap1. Jeżeli jednocześnie aktywne będzie więcej niż jeden ewaluator należący do tej samej grupy, biblioteka OpenGL wygeneruje wartości ewaluatora zawierającego większą ilość współrzędnych.
Renderowanie krzywej
Biblioteka OpenGL udostępnia kilka sposobów wykorzystania danych wygenerowanych przez wybrany ewaluator jednowymiarowy. Najbardziej elementarną metodą jest użycie funkcji z grupy glEvalCoord1:
void glEvalCoord1d( GLdouble u )
void glEvalCoord1f( GLfloat u )
void glEvalCoord1dv( const GLdouble * u )
void glEvalCoord1fv( const GLfloat * u )
które zwracają wartości wygenerowane przez ewaluator dla danego parametru u funkcji parametrycznej P (u) (przypomnijmy, że typowo dziedziną tej funkcji są liczby z przedziału [0, 1]).
Wywołanie funkcji z grupy glEvalCoord1 jest równoważne odpowiednim wywołaniom funkcji z grup: glVertex, glColor, glIndex, glNormal i glTexCoord, przy czym jeżeli aktywnych jest jednocześnie kilka ewaluatorów zawsze jako ostatni będą generowane współrzędne wierzchołków. Jak się Czytelnik domyśla użycie funkcji z grupy glEvalCoord1 nastąpi najczęściej pomiędzy wywołaniami funkcji glBegin/glEnd.
Jeżeli nie chcemy samodzielnie renderować krzywej z danych wygenerowanych przez ewaluator trzeba skorzystać z funkcji z grupy glMapGrid1:
void glMapGrid1d( GLint un, GLdouble u1, GLdouble u2 )
void glMapGrid1f( GLint un, GLfloat u1, GLfloat u2 )
których zadaniem jest utworzenie mapy (siatki) punktów odwzorowujących krzywą. Ilość generowanych punktów określa parametr un, a u1 i u2 wyznaczają przedział wartości funkcji parametrycznej P (u), dla których generowane są punkty mapy.
Tak uzyskane punkty mapy można renderować korzystając z funkcji glEvalMesh1:
void glEvalMesh1( GLenum mode, GLint i1, GLint i2 )
gdzie parametr mode określa tryb renderingu (możliwe są
GL_POINT - punkty i
GL_LINE - linia), a parametry i1 i i2 określają numery renderowanych punktów mapy.
Alternatywnie można także narysować pojedynczy punkt mapy korzystając z funkcji glEvalPoint1:
void glEvalPoint1( GLint i )
Powierzchnie B´eziera
Powierzchnie B´eziera są parametrycznymi określonymi funkcją dwuparametrową P (u, v):
gdzie u i v przyjmują wartości z przedziału [0, 1].
Zasady tworzenia powierzchni B´eziera są identyczne jak tworzenie krzywych. Opisane wyżej funkcje mają swoje odpowiedniki, które mają w nazwie cyfrę „2”, ale oczywiście różnią się także ilością parametrów.
Ewaluatory dwuwymiarowe
Ewaluator dwuwymiarowy definiują funkcje z grupy glMap2:
void glMap2d( GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble * points )
void glMap2f( GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat * points )
Parametr target określa rodzaj wartości generowanych przez ewaluator. Dopuszczalne są następujące wartości tego parametru:
Podobnie jak w przypadku ewaluatorów jednowymiarowych Współrzędne tekstur generowane są wyłącznie dla pierwszej jednostki teksturującej (
GL_TEXTURE0). Jeżeli aktywną jednostką teksturującą nie jest
GL_TEXTURE0 zostanie wygenerowany błąd
GL_INVALID_OPERATION.
Parametry u1, u2 oraz v1, v2 określają odpowiednio przedziały wartości parametrów u i v funkcji parametrycznej P (u, v). Typowo wykorzystywany jest cały przedział wartości obu parametrów czyli [0, 1].
Parametry ustride i vstride określają ilości wartości zmiennoprzecinkowych (odpowiednio GLfloat lub GLdouble) zawartych pomiędzy danymi poszczególnych punktów kontrolnych odpowiednio dla współrzędnych u i v. Umożliwia to umieszczenie w tablicy points innych danych poza współrzędnymi punktów kontrolnych.
Kolejne parametry uorder i vorder określają ilości punktów kontrolnych krzywej dla współrzędnych u i v zawartych w tablicy points. Dla każdego z tych parametrów obowiązuje opisane już ograniczenie maksymalnej ilości punktów kontrolnych obsługiwanych przez daną implementację OpenGL, określone w zmiennej stanu
GL_MAX_EVAL_ORDER.
Korzystanie z ewaluatorów dwuwymiarowych wymaga ich wcześniejszej aktywacji przy użyciu funkcji glEnable. Podobnie jak w przypadku ewaluatorów jednowymiarowych, jednoczesna aktywacja dwóch lub większej ilości ewaluatorów należących do tej samej grupy, spowoduje, że biblioteka OpenGL wygeneruje wartości ewaluatora zawierającego większą ilość współrzędnych.
Dodajmy, że biblioteka OpenGL umożliwia włączenie automatycznego generowania wektorów normalnych zarówno dla powierzchni jak i krzywych B´eziera. Wymaga to wywołania funkcji glEnable z parametrem
GL_AUTO_NORMAL.
Rendering powierzchni
Podobnie jak w przypadku krzywych B´eziera także w przypadku powierzchni biblioteka OpenGL udostępnia kilka sposobów wykorzystania danych wygenerowanych przez wybrany ewaluator dwuwymiarowy. Elementarną metodą jest użycie funkcji z grupy glEvalCoord2:
void glEvalCoord2dv( const GLdouble * u )
void glEvalCoord2fv( const GLfloat * u )
void glEvalCoord2d( GLdouble u, GLdouble v )
void glEvalCoord2f( GLfloat u, GLfloat v )
które zwracają wartości wygenerowane przez ewaluator dla danej pary parametrów (u, v) funkcji parametrycznej P (u, v). Tablice wskazywane w parametrze u funkcji glEvalCoord2dv i glEvalCoord2fv zawierają parami wartości u i v.
Ponieważ wywołanie funkcji z grupy glEvalCoord2 jest równoważne odpowiednim wywołaniom funkcji z grup: glVertex, glColor, glIndex, glNormal i glTexCoord (jako ostatnie generowane są współrzędne wierzchołków), ich użycie nastąpi najczęściej pomiędzy funkcjami glBegin/glEnd.
Alternatywnie można utworzyć mapę (siatkę) punktów odwzorowujących powierzchnię B´eziera, co wymaga użycia funkcji z grupy glMapGrid2:
void glMapGrid2d( GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2 )
void glMapGrid2f( GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2 )
Parametry un i vn określają ilość generowanych punktów mapy odpowiednio dla parametrów u i v funkcji parametrycznej P (u, v), natomiast pary parametrów u1, u2 i v1, v2 wyznaczają przedziały wartości u oraz v, dla których generowane są punkty mapy.
Uzyskane w powyższy sposób punkty mapy można bezpośrednio renderować korzystając z funkcji glEvalMesh2:
void glEvalMesh2( GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2 )
której parametr mode określa tryb renderingu (możliwe są:
GL_POINT - punkty,
GL_LINE - linia i
GL_FILL - wypełnienie), a pary parametrów i1, i2 oraz j1, j2 określają numery renderowanych punktów mapy, odpowiednio dla parametrów u i v funkcji P (u, v).
Można także narysować pojedynczy punkt mapy korzystając z funkcji glEvalPoint2:
void glEvalPoint2( GLint i, GLint j )
Pobieranie danych map
Pobranie parametrów odwzorowania (map) określonych funkcjami z grupy glMap1 i glMap2 umożliwiają funkcje glGetMap:
void glGetMapdv( GLenum target, GLenum query, GLdouble * v )
void glGetMapfv( GLenum target, GLenum query, GLfloat * v )
void glGetMapiv( GLenum target, GLenum query, GLint * v )
Parametr target określa rodzaj odwzorowania, którego dane będą pobierane. Możliwe są następujące wartości:
Drugi parametr query określa jakie parametry odwzorowania mają zostać zwrócone w tablicy v. Możliwe są trzy wartości tego parametru:
Programy przykładowe
Pierwszy program przykładowy (plik krzywa beziera.cpp) przedstawia krzywą B´eziera opartą o cztery punkty kontrolne. Początkowy wygląd krzywej, przedstawiony na rysunku 1, można modyfikować przemieszczając punkty kontrolne. Przykładowy efekt takiej modyfikacji przedstawia rysunek 1. Do przesuwania punktów kontrolnych wykorzystano oczywiście bufor selekcji.
Plik krzywa_beziera.cpp
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "colors.h"
enum
{
EXIT
};
int button_state = GLUT_UP;
int button_x, button_y;
GLfloat points[ 12 ] =
{
50.0, 50.0, 0.0, 50.0, 450.0, 0.0, 450.0, 50.0, 0.0, 450.0, 450.0, 0.0
};
enum
{
NONE,
CURVE,
POINT_0,
POINT_1,
POINT_2,
POINT_3
};
int select_object = NONE;
void Display()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT );
glInitNames();
glPushName( NONE );
glLoadName( CURVE );
glEnable( GL_MAP1_VERTEX_3 );
glMap1f( GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, points );
glLineWidth( 2.0 );
glColor3fv( Black );
glBegin( GL_LINE_STRIP );
for( int i = 0; i <= 25; i++ )
glEvalCoord1f(( GLfloat ) i / 25.0 );
glEnd();
glColor3fv( Red );
glLoadName( POINT_0 );
glRectf( points[ 0 ] - 5, points[ 1 ] - 5, points[ 0 ] + 5, points[ 1 ] + 5 );
glLoadName( POINT_1 );
glRectf( points[ 3 ] - 5, points[ 4 ] - 5, points[ 3 ] + 5, points[ 4 ] + 5 );
glLoadName( POINT_2 );
glRectf( points[ 6 ] - 5, points[ 7 ] - 5, points[ 6 ] + 5, points[ 7 ] + 5 );
glLoadName( POINT_3 );
glRectf( points[ 9 ] - 5, points[ 10 ] - 5, points[ 9 ] + 5, points[ 10 ] + 5 );
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D( 0, width, 0, height );
Display();
}
void Selection( int x, int y )
{
const int BUFFER_LENGTH = 64;
GLuint select_buffer[ BUFFER_LENGTH ];
glSelectBuffer( BUFFER_LENGTH, select_buffer );
int viewport[ 4 ];
glGetIntegerv( GL_VIEWPORT, viewport );
int width = viewport[ 2 ];
int height = viewport[ 3 ];
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
gluPickMatrix( x, height - y, 2, 2, viewport );
gluOrtho2D( 0, width, 0, height );
glRenderMode( GL_SELECT );
Display();
GLint hits = glRenderMode( GL_RENDER );
glMatrixMode( GL_PROJECTION );
glPopMatrix();
select_object = NONE;
if( hits > 0 )
if( select_buffer[ 3 ] > CURVE )
select_object = select_buffer[ 3 ];
}
void MouseButton( int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON )
{
Selection( x, y );
glutPostRedisplay();
button_state = state;
if( state == GLUT_DOWN )
{
button_x = x;
button_y = y;
}
}
}
void MouseMotion( int x, int y )
{
if( x > 0 & x < glutGet( GLUT_WINDOW_WIDTH ) & y > 0 & y < glutGet( GLUT_WINDOW_HEIGHT ) )
if( button_state == GLUT_DOWN )
{
switch( select_object )
{
case POINT_0:
points[ 0 ] += x - button_x;
points[ 1 ] += button_y - y;
break;
case POINT_1:
points[ 3 ] += x - button_x;
points[ 4 ] += button_y - y;
break;
case POINT_2:
points[ 6 ] += x - button_x;
points[ 7 ] += button_y - y;
break;
case POINT_3:
points[ 9 ] += x - button_x;
points[ 10 ] += button_y - y;
break;
}
button_x = x;
button_y = y;
glutPostRedisplay();
}
}
void Menu( int value )
{
switch( value )
{
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
glutInitWindowSize( 500, 500 );
#ifdef WIN32
glutCreateWindow( "Krzywa Béziera" );
#else
glutCreateWindow( "Krzywa Beziera" );
#endif
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
glutMouseFunc( MouseButton );
glutMotionFunc( MouseMotion );
glutCreateMenu( Menu );
glutCreateMenu( Menu );
#ifdef WIN32
glutAddMenuEntry( "Wyjście", EXIT );
#else
glutAddMenuEntry( "Wyjscie", EXIT );
#endif
glutAttachMenu( GLUT_RIGHT_BUTTON );
glutMainLoop();
return 0;
}
Drugi program przykładowy (plik powierzchnia beziera.cpp) rysuje powierzchnię B´eziera opartą na 12 punktach kontrolnych. W odróżnieniu od poprzedniego programu nie ma możliwości bezpośredniej zmiany płożena punktów kontrolnych, ale za to powierzchnię można renderować w różny sposób: w formie punktów siatki, linii siatki (rysunek 3), wypełnienia (rysunek 4) oraz pokrytej teksturą (rysunek 5). W przypadku powierzchni wypełnionej program korzysta dodatkowo z oświetlenia (standardowe źródło światła
GL_LIGHT0) oraz znanych już z wcześniejszych programów definicji materiałów.
Szczególną uwagę warto zwrócić na sposób teksturowania powierzchni B´eziera. Wymaga to bowiem zdefiniowania dodatkowej serii punktów kontrolnych, tym razem zawierających współrzędne tekstury. Trzeba także uaktywnić odpowiedni ewaluator dwuwymiarowy.
Plik powierzchnia_beziera.cpp
#include <GL/glut.h>
#include <GL/glu.h>
#include <stdlib.h>
#include "colors.h"
#include "materials.h"
enum
{
BRASS,
BRONZE,
POLISHED_BRONZE,
CHROME,
COPPER,
POLISHED_COPPER,
GOLD,
POLISHED_GOLD,
PEWTER,
SILVER,
POLISHED_SILVER,
EMERALD,
JADE,
OBSIDIAN,
PEARL,
RUBY,
TURQUOISE,
BLACK_PLASTIC,
BLACK_RUBBER,
FULL_WINDOW,
ASPECT_1_1,
EXIT
};
int aspect = FULL_WINDOW;
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;
GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;
int button_state = GLUT_UP;
int button_x, button_y;
GLfloat scale = 1.0;
const GLfloat * ambient = BrassAmbient;
const GLfloat * diffuse = BrassDiffuse;
const GLfloat * specular = BrassSpecular;
GLfloat shininess = BrassShininess;
int style = GL_FILL;
GLfloat points[ 12 * 3 ] =
{
- 1.0, - 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0,
- 1.0, - 1.0, 0.0, - 1.0, 1.0, 0.0, 1.0, - 1.0, 0.0, 1.0, 1.0, 0.0,
- 1.0, - 1.0, - 1.0, - 1.0, 1.0, - 1.0, 1.0, - 1.0, - 1.0, 1.0, 1.0, - 1.0
};
GLfloat texture_coord[ 4 * 2 ] =
{
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0
};
GLubyte polish_flag[ 2 * 2 * 3 ] =
{
0xD9, 0x1E, 0x41, 0xD9, 0x1E, 0x41,
0xF2, 0xE6, 0xEF, 0xF2, 0xE6, 0xEF
};
void Display()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glEnable( GL_DEPTH_TEST );
glTranslatef( 0, 0, -( near + far ) / 2 );
glRotatef( rotatex, 1.0, 0, 0 );
glRotatef( rotatey, 0, 1.0, 0 );
glScalef( scale, scale, scale );
glEnable( GL_MAP2_VERTEX_3 );
glMap2f( GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 3, points );
glMapGrid2f( 25, 0.0, 1.0, 25, 0.0, 1.0 );
if( style == GL_POINT )
{
glColor3fv( Black );
glPointSize( 2.0 );
glEvalMesh2( GL_POINT, 0, 25, 0, 25 );
}
if( style == GL_LINE )
{
glColor3fv( Black );
glLineWidth( 2.0 );
glEvalMesh2( GL_LINE, 0, 25, 0, 25 );
}
if( style == GL_FILL )
{
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
glEnable( GL_NORMALIZE );
glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, ambient );
glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse );
glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular );
glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, shininess );
glEnable( GL_AUTO_NORMAL );
glEvalMesh2( GL_FILL, 0, 25, 0, 25 );
glDisable( GL_AUTO_NORMAL );
glDisable( GL_NORMALIZE );
glDisable( GL_LIGHTING );
}
if( style == GL_TEXTURE )
{
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, polish_flag );
glEnable( GL_TEXTURE_2D );
glEnable( GL_MAP2_TEXTURE_COORD_2 );
glMap2f( GL_MAP2_TEXTURE_COORD_2, 0.0, 1.0, 2, 2, 0.0, 1.0, 4, 2, texture_coord );
glMapGrid2f( 25, 0.0, 1.0, 25, 0.0, 1.0 );
glEvalMesh2( GL_FILL, 0, 25, 0, 25 );
glDisable( GL_MAP2_TEXTURE_COORD_2 );
glDisable( GL_TEXTURE_2D );
}
glColor3fv( Blue );
glPointSize( 6.0 );
glBegin( GL_POINTS );
for( int i = 0; i < 12; i++ )
glVertex3fv( points + i * 3 );
glEnd();
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
if( aspect == ASPECT_1_1 )
{
if( width < height && width > 0 )
glFrustum( left, right, bottom * height / width, top * height / width, near, far );
else
if( width >= height && height > 0 )
glFrustum( left * width / height, right * width / height, bottom, top, near, far );
}
else
glFrustum( left, right, bottom, top, near, far );
Display();
}
void Keyboard( unsigned char key, int x, int y )
{
if( key == '+' )
scale += 0.05;
else
if( key == '-' && scale > 0.05 )
scale -= 0.05;
Display();
}
void MouseButton( int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON )
{
button_state = state;
if( state == GLUT_DOWN )
{
button_x = x;
button_y = y;
}
}
}
void MouseMotion( int x, int y )
{
if( button_state == GLUT_DOWN )
{
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();
}
}
void Menu( int value )
{
switch( value )
{
case GL_POINT:
case GL_LINE:
case GL_FILL:
case GL_TEXTURE:
style = value;
Display();
break;
case BRASS:
ambient = BrassAmbient;
diffuse = BrassDiffuse;
specular = BrassSpecular;
shininess = BrassShininess;
Display();
break;
case BRONZE:
ambient = BronzeAmbient;
diffuse = BronzeDiffuse;
specular = BronzeSpecular;
shininess = BronzeShininess;
Display();
break;
case POLISHED_BRONZE:
ambient = PolishedBronzeAmbient;
diffuse = PolishedBronzeDiffuse;
specular = PolishedBronzeSpecular;
shininess = PolishedBronzeShininess;
Display();
break;
case CHROME:
ambient = ChromeAmbient;
diffuse = ChromeDiffuse;
specular = ChromeSpecular;
shininess = ChromeShininess;
Display();
break;
case COPPER:
ambient = CopperAmbient;
diffuse = CopperDiffuse;
specular = CopperSpecular;
shininess = CopperShininess;
Display();
break;
case POLISHED_COPPER:
ambient = PolishedCopperAmbient;
diffuse = PolishedCopperDiffuse;
specular = PolishedCopperSpecular;
shininess = PolishedCopperShininess;
Display();
break;
case GOLD:
ambient = GoldAmbient;
diffuse = GoldDiffuse;
specular = GoldSpecular;
shininess = GoldShininess;
Display();
break;
case POLISHED_GOLD:
ambient = PolishedGoldAmbient;
diffuse = PolishedGoldDiffuse;
specular = PolishedGoldSpecular;
shininess = PolishedGoldShininess;
Display();
break;
case PEWTER:
ambient = PewterAmbient;
diffuse = PewterDiffuse;
specular = PewterSpecular;
shininess = PewterShininess;
Display();
break;
case SILVER:
ambient = SilverAmbient;
diffuse = SilverDiffuse;
specular = SilverSpecular;
shininess = SilverShininess;
Display();
break;
case POLISHED_SILVER:
ambient = PolishedSilverAmbient;
diffuse = PolishedSilverDiffuse;
specular = PolishedSilverSpecular;
shininess = PolishedSilverShininess;
Display();
break;
case EMERALD:
ambient = EmeraldAmbient;
diffuse = EmeraldDiffuse;
specular = EmeraldSpecular;
shininess = EmeraldShininess;
Display();
break;
case JADE:
ambient = JadeAmbient;
diffuse = JadeDiffuse;
specular = JadeSpecular;
shininess = JadeShininess;
Display();
break;
case OBSIDIAN:
ambient = ObsidianAmbient;
diffuse = ObsidianDiffuse;
specular = ObsidianSpecular;
shininess = ObsidianShininess;
Display();
break;
case PEARL:
ambient = PearlAmbient;
diffuse = PearlDiffuse;
specular = PearlSpecular;
shininess = PearlShininess;
Display();
break;
case RUBY:
ambient = RubyAmbient;
diffuse = RubyDiffuse;
specular = RubySpecular;
shininess = RubyShininess;
Display();
break;
case TURQUOISE:
ambient = TurquoiseAmbient;
diffuse = TurquoiseDiffuse;
specular = TurquoiseSpecular;
shininess = TurquoiseShininess;
Display();
break;
case BLACK_PLASTIC:
ambient = BlackPlasticAmbient;
diffuse = BlackPlasticDiffuse;
specular = BlackPlasticSpecular;
shininess = BlackPlasticShininess;
Display();
break;
case BLACK_RUBBER:
ambient = BlackRubberAmbient;
diffuse = BlackRubberDiffuse;
specular = BlackRubberSpecular;
shininess = BlackRubberShininess;
Display();
break;
case FULL_WINDOW:
aspect = FULL_WINDOW;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case ASPECT_1_1:
aspect = ASPECT_1_1;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
glutInitWindowSize( 500, 500 );
#ifdef WIN32
glutCreateWindow( "Powierzchnia Béziera" );
#else
glutCreateWindow( "Powierzchnia Beziera" );
#endif
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
glutKeyboardFunc( Keyboard );
glutMouseFunc( MouseButton );
glutMotionFunc( MouseMotion );
glutCreateMenu( Menu );
int MenuStyle = glutCreateMenu( Menu );
glutAddMenuEntry( "GL_POINT", GL_POINT );
glutAddMenuEntry( "GL_LINE", GL_LINE );
glutAddMenuEntry( "GL_FILL", GL_FILL );
glutAddMenuEntry( "GL_FILL + tekstura", GL_TEXTURE );
int MenuMaterial = glutCreateMenu( Menu );
#ifdef WIN32
glutAddMenuEntry( "Mosiądz", BRASS );
glutAddMenuEntry( "Brąz", BRONZE );
glutAddMenuEntry( "Polerowany brąz", POLISHED_BRONZE );
glutAddMenuEntry( "Chrom", CHROME );
glutAddMenuEntry( "Miedź", COPPER );
glutAddMenuEntry( "Polerowana miedź", POLISHED_COPPER );
glutAddMenuEntry( "Złoto", GOLD );
glutAddMenuEntry( "Polerowane złoto", POLISHED_GOLD );
glutAddMenuEntry( "Grafit (cyna z ołowiem)", PEWTER );
glutAddMenuEntry( "Srebro", SILVER );
glutAddMenuEntry( "Polerowane srebro", POLISHED_SILVER );
glutAddMenuEntry( "Szmaragd", EMERALD );
glutAddMenuEntry( "Jadeit", JADE );
glutAddMenuEntry( "Obsydian", OBSIDIAN );
glutAddMenuEntry( "Perła", PEARL );
glutAddMenuEntry( "Rubin", RUBY );
glutAddMenuEntry( "Turkus", TURQUOISE );
glutAddMenuEntry( "Czarny plastik", BLACK_PLASTIC );
glutAddMenuEntry( "Czarna guma", BLACK_RUBBER );
#else
glutAddMenuEntry( "Mosiadz", BRASS );
glutAddMenuEntry( "Braz", BRONZE );
glutAddMenuEntry( "Polerowany braz", POLISHED_BRONZE );
glutAddMenuEntry( "Chrom", CHROME );
glutAddMenuEntry( "Miedz", COPPER );
glutAddMenuEntry( "Polerowana miedz", POLISHED_COPPER );
glutAddMenuEntry( "Zloto", GOLD );
glutAddMenuEntry( "Polerowane zloto", POLISHED_GOLD );
glutAddMenuEntry( "Grafit (cyna z ołowiem)", PEWTER );
glutAddMenuEntry( "Srebro", SILVER );
glutAddMenuEntry( "Polerowane srebro", POLISHED_SILVER );
glutAddMenuEntry( "Szmaragd", EMERALD );
glutAddMenuEntry( "Jadeit", JADE );
glutAddMenuEntry( "Obsydian", OBSIDIAN );
glutAddMenuEntry( "Perla", PEARL );
glutAddMenuEntry( "Rubin", RUBY );
glutAddMenuEntry( "Turkus", TURQUOISE );
glutAddMenuEntry( "Czarny plastik", BLACK_PLASTIC );
glutAddMenuEntry( "Czarna guma", BLACK_RUBBER );
#endif
int MenuAspect = glutCreateMenu( Menu );
#ifdef WIN32
glutAddMenuEntry( "Aspekt obrazu - całe okno", FULL_WINDOW );
#else
glutAddMenuEntry( "Aspekt obrazu - cale okno", FULL_WINDOW );
#endif
glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
glutCreateMenu( Menu );
glutAddSubMenu( "Styl", MenuStyle );
#ifdef WIN32
glutAddSubMenu( "Materiał", MenuMaterial );
glutAddSubMenu( "Aspekt obrazu", MenuAspect );
glutAddMenuEntry( "Wyjście", EXIT );
#else
glutAddSubMenu( "Material", MenuMaterial );
glutAddSubMenu( "Aspekt obrazu", MenuAspect );
glutAddMenuEntry( "Wyjscie", EXIT );
#endif
glutAttachMenu( GLUT_RIGHT_BUTTON );
glutMainLoop();
return 0;
}