- {$MODE OBJFPC}
- {
- Vorab: Obwohl ich didaktisch kein Genie nicht so gut bin hoffe ich dass dieses Ding nützlich ist.
- Und nun zu Sache:
- FreePascal, dieser kleine, schlanke 32-Bit (Object-)pascalcompiler erfreut sich immer mehr Belibetheit. Einerseits wegen der vielen unterstützten Systeme, andererseits weil er kostenlos ist. und alle selbst geschriebenen Programme frei veröffentlicht werden dürfen. Bei vielen jedoch fristet er ein Schattendasein als Turbo-Pascal-Ersatz. Dass FreePascal jedoch weitgehend über Delphis Object Pascal Syntax verfügt, und mit der FreePascal Component Library (Welche selbst Units für MySQL, ZIP Dateien, XML beinhaltet) mächtige Werkzeuge zur Verfügung stehen, ist eher unbekannt. Im Zuge dessen wurden auch Delphi Bibliotheken wie die Unit "Classes" (TStream, TFileStream, etc.) nachprogrammiert. Die implementation der Delphi Objektorientierung machts möglich.
- }
- //TIP: {$MODE OBJFPC} oder {$MODE DELPHI} nehmen, damit werden Delphi Klassen unterstützt.
- {
- Es gibt also viele Möglichkeiten, in denen FreePascal anwendbar ist. Eine weitere ist ? trommelwirbel - _OpenGL_ ? worum sich dieses Tutorial dreht.
- Dieses Tutorial-im-Quelltext soll dir zeigen, was du zum Programmieren von OpenGL mit FreePascal brauchst, und wie du es angehen kannst, ich hoffe du hast deine Freude daran mit den Quelltexten herumzuspielen, und ein verständnis für OpenGL zu erlangen, bevor du beginnst solltest du aber wissen, dass es nicht einfach war dieses Tutorial zu schreiben, ich stand nähmlich vor der Frage, welchen Wegder Initialisierung ich zeigen sollte, zur Auswahl stehen:
- o SDL (Simple Direct Media Layer, oder so), GLUT
- o Glut
- o WinAPI
- o GtkGlArea (soweit ich weiß)
- Ich will nun nicht das ganze für und wieder durchkauen, ich habe mich für SDL entschieden, denn SDL hat bietet neben der OpenGL Initialisierung und dem Event- Handling noch Sound - Unterstützung und viel mehr, das ganze Plattformunabhängig. Leider ist es auch komplex, deshalb weise ich auch ausdrücklich darauf hin, dass es bessere Initialisierungswege geben mag, und das dieser Quelltext keine Universallösung sein muss. Für die ersten Beispiele tut er es auf jeden Fall.
- Dieses Tutorial sollte auf jeden Fall in Verbindung mit Tutorial1 und Tutorial2 von delphigl.com genossen werden. Ich werde nicht auf die OpenGL Befehle eingehen.
- Was du also neben Pascal Grundkenntnissen benötigst ist:
- o SDL4FreePascal in Version 1.2 oder höher (JediSDL frisst mein FreePascal Compiler nicht). Mit Google leicht zufinden. Außerdem noch die SDL Runtime Library - www.libsdl.org.
- o eine neue OpenGL Unit Version von FreePascal, erreichbar über den CVS Server, da die alten unvollständig sind.
- o Eine aktuelle Version des FreePascal Compilers (1.0.6) für Windows oder Linux (wenn die OpenGL Units auch unter BeOS laufen wüsste ichs gerne). By the way: Wenn jemand mir sagen könnte dób dieses Beispiel auch Problemlos auf Linux läuft wär's gut.
- Jetzt aber ran an die Arbeit!!!
- }
- program
- OpenGLmitFreePascal;
- {
- Als erstes werden wir unsere Units einbinden, es sind:
- }
- uses
- gl, {OpenGL Befehle}
- glu, {OpenGL Hilfsbefehle}
- sdl, {SDL, unsere API}
- sdl_video, {Wir wollen mit Graphik arbeiten}
- sdl_events, {Und Ereignisse behandeln}
- sdl_types, {Grundlegende Typen}
- Crt; {Wegen unserer netten KeyPressed Funktion}
- {Jetzt geht es richtig los.
- Ich rechne an dieser Stelle fest damit, dass einige ein reserviertes Attribut von Prozeduren/Funktionen nicht kennen, es nennt sich forward. Wenn eine Prozedur mit diesem Attribut versehen wird, muss sie erst später im Quelltext definiert werden. Ich nutze es gerne, um einen Überblick über meine Programme zu erhalten.
- Wir benötigen mehrere Prozeduren, ich habe sie alle mit Forward deklariert:}
- procedure ResizeSurface(width, height : Integer); forward; {Eine Prozedur, die aufgerufen wird, wenn die Größe des Fensters geändert wird}
- procedure DrawGL; forward; {In dieser Prozedur werden die Sachen gezeichnet}
- procedure InitGL; forward; {In dieser Prozedur wir OpenGL Initialisiert}
- procedure Escape(Meldung : ansistring); forward; {Und noch eine kleine Prozedur, die das Programm beendet, wenn ein Fehler Auftritt.}
- {Bevor wir uns jedoch um den Inhalt der Prozeduren kümmern, sollten wir uns um die Initialisierung kümmern. Scrolle bitte bis zur Variablendeklaration}
- {Folgendes habe ich fast 1:1 aus Phoebus' Tutorial.}
- procedure ResizeSurface(width, height : Integer);
- begin
- if (Height = 0) then Height := 1;
- glViewport(0, 0, Width, Height); // Setzt den Viewport für das OpenGL Fenster
- glMatrixMode(GL_PROJECTION); // Matrix Mode auf Projection setzen
- glLoadIdentity(); // Reset View
- gluPerspective(45.0, Width/Height, 1.0, 100.0); // Perspektive den neuen Maßen anpassen.
- glMatrixMode(GL_MODELVIEW); // Zurück zur Modelview Matrix
- glLoadIdentity(); // Reset View
- end;
- procedure DrawGL;
- begin
- glClearColor(0,0,0,0);
- glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
- glLoadIdentity;
- glTranslatef( -1.5, 0, -6);
- {TIP: Entferne die beiden oberen glColor3f. Das Dreieck wird dann "blau" gezeichnet
- werden! Wenn Sie beim starten ganz schnell gucken, wirst du feststellen, dass das
- Dreieck kurz weiß zu flackern scheint. Der Grund dafür ist, dass beim ersten Durchgang
- die beiden ersten Eckpunkte weiß gezeichnet werden und erst beim nächsten Durchgang
- blau gefärbt werden. Um dies zu vermeiden müßte man bereits vor dem Zeichnen dieser
- beiden Eckpunkte die Farbe auf blau setzen!}
- glBegin(GL_TRIANGLES);
- glColor3f(1,0,0); // alle weiteren Vertice werden rot gezeichnet
- glVertex3f(-1.0,-1.0, 0.0);
- glColor3f(0,1,0); // alle weiteren Verticen werden grün gezeichnet
- glVertex3f( 0.0, 1.0, 0.0);
- glColor3f(0,0,1); // alle weiteren Verticen werden blau gezeichnet
- glVertex3f( 1.0,-1.0, 0.0);
- glEnd();
- end;
- procedure InitGL;
- begin
- glEnable(GL_TEXTURE_2D); // Aktiviert Texture Mapping
- glShadeModel(GL_SMOOTH); // Aktiviert weiches Shading
- glClearColor(0.0, 0.0, 0.0, 0.5); // Bildschirm löschen (schwarz)
- glClearDepth(1.0); // Depth Buffer Setup
- glEnable(GL_DEPTH_TEST); // Aktiviert Depth Testing
- glDepthFunc(GL_LEQUAL); // Bestimmt den Typ des Depth Testing
- glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
- // Qualitativ bessere Koordinaten Interpolation
- end;
- procedure Escape(Meldung : ansistring); {Wenn was schief läuft gibt's eine Fehlermeldung, es wird auf Tastendruck geartet und wir verabschieden uns.}
- begin
- writeln(Meldung);
- repeat until Keypressed;
- halt;
- end;
- var
- videoInfo : PSDL_VideoInfo; {Zeiger auf Informationen zur Graphikfähigkeit der Hardware}
- zeichenflaeche : PSDL_Surface; {Zeiger auf die Daten unserer Zeichenfläche}
- event : SDL_Event; {hier wird gespeichert, welche Events es gibt}
- videoflags : uint32; {Einige Eigenschaften die wir unserer Zeichenfläche auf den Weg geben, wenn sie Initialisiert wird.}
- Done : Boolean;
- begin
- {
- Die Initialisierung von SDL wird mit SDL_Init(flags : uint32) : Integer durchgeführt. uint32 ist ein vorzeichenloser (u = unsigned) Integertyp ( in c int), auch Cardinal genannt. Der zurückgegebene Wert ist 0, wenn erfolgreich Initialisiert wurde, ansonsten trat ein fehler auf. Die Flags sind die Modi die Initialisiert werden sollen.
- Wir initialisieren nun den Video Modus, SDL_INIT_VIDEO , und prüfen ob er initialisiert wurde. Wenn nicht, wird das Programm beendet. Weitere Flags werden mit or Verbunden.}
- if SDL_Init(SDL_INIT_VIDEO) < 0 then
- Escape('SDL_Init(SDL_INIT_VIDEO) schlug fehl');
- {Als nächstes Sammeln wir Informationen über die Graphische Ausstattung unseres PCs}
- videoInfo := SDL_GetVideoInfo;
- {Und prüfen nun ob wir Hardware nutzen können, um unserer Zeichenfläche Hardwareunterstützung zukommen zu lassen, ich gebe zu, ich steige hir nicht ganz genau durch. "videoFlags" wird nachher bei der Initialisierung des SDL_Surfaces übergeben. Viele Einstellungen können über dieses Flag gemacht werden.}
- if hw_available(VideoInfo^) <> 0 then
- videoFlags := SDL_HWSURFACE {Graphikkarte}
- else
- videoFlags := SDL_SWSURFACE; {normales Ram}
- {Warum haben wir 3D Graphikkarten? Wegen der Hardwareunterstützung Ob die entsprechende Karte diese besitzt prüfen wir, wenn ja sagen wir, dass wir Hardwarebeschleunigung nutzen wollen.}
- if blit_hw(VideoInfo^) <> 0 then
- videoFlags := videoFlags or SDL_HWACCEL;
- {Unser Surface soll OpenGL Unterstützen, die und die größe soll geändert werden können.}
- videoflags := SDL_OPENGL or SDL_HWPALETTE or SDL_RESIZABLE;
- {
- SDL_GL_SetAttribute(attr : SDL_GLattr; value : Integer): Integer;
- Auch ein gebiet, dass ich, ehrlich gesagt noch nicht stark gestreift habe, vorerst sollte reichen, dass wir es brauchen. Wer genaueres weiß sollte ja nicht schweigen.
- }
- SDL_GL_SetAttribute(SDL_GL_RED_SIZE,5);
- SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,5);
- SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,5);
- SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,16);
- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
- {Nun, endlich öffnen wir unser Fenster, und zwar mit
- SDL_SetVideoMODE(width,height,bpp : Integer; flags : uint32): PSDL_Surface;
- width, height sind klar, bpp ist die Farbtiefe, mit flags übergibet man die zuvor definierten Eigenschaften.
- Als Rückgabewert erhält man einen Zeiger auf ein SDL_Surface, was wir mit einem PSDL_SURFACE auffangen.}
- zeichenflaeche := SDL_SetVideoMode(640,480,16,VideoFlags);
- {Wenn ein Fehler passiert, kann der Rückgabewert nil sein, dann können wir unser Fenster vergessen, und müssen strategischen Rückzug antreten.}
- if zeichenflaeche = nil then
- Escape('Es ist ein Fehler bei der initialisierung der Zeichenflaeche aufgetreten, das PSDL_Surface ist nil');
- {Nun ist der Zeitpunkzt gekommen OpenGL selbst zu Initialisieren}
- InitGL;
- {Das erstellen unserer Zeichenflaeche kommt einer Veränderung der Größe der Zeichenflaeche gleich, deshalb:}
- ResizeSurface(zeichenflaeche^.w, {SDL_Surface ist ein Record, das Attribut w gibt die breite (engl. width) an}
- zeichenflaeche^.h); {h gibt die Höhe an}
- {Und schließlich kommen wir in die Eventschleife, zuvor setzen wir noch done auf false}
- Done := false;
- while not Done do {während done false ist }
- begin
- while SDL_PollEvent(@event) <> 0 do {Solange Events da sind, wird die Schleife wiederhohlt}
- case event.eventtype of
- SDL_EVENTQUIT: {Jemand will dass Programm beenden}
- Done := true; {Schleife wird damit nicht erneut durchlaufen, das Programm wird fortgesetzt, in unserem Fall danach beendet.}
- SDL_VIDEORESIZE: {Die Fenstergröße wird geändert}
- ResizeSurface(event.resize.w,event.resize.h); {Wir reagieren darauf mit unserer Prozedur, die Parameter enthalten die aktuelle Größe des Fensters}
- end;
- DrawGL; {Unsere Zeichenfunktion, sie Zeichnet die Elemente}
- {Nun benötigen wir noch einen Befehl, der alles auf den Bildschirm bringt, SDL_GL_SWAPBUFFERS;}
- SDL_GL_SwapBuffers;
- end;
- {SDL deinitialisieren (kreative Wortschöpfung)}
- SDL_Quit;
- end.
- {springe nun zurück, hinter den forward-Deklarationen schreiben wir nun die Prozedur- und Funktionsinhalte}