Buchempfehlung
Visual Basic 6 Kochbuch
Visual Basic 6 Kochbuch
Viele praktische Tipps zum Programmieren mit Visual Basic 6, die sich oft auch auf FB übertragen lassen. [Mehr Infos...]
FreeBASIC-Chat
Es sind Benutzer im FreeBASIC-Chat online.
(Stand:  )
FreeBASIC bei Twitter
Twitter FreeBASIC-Nachrichten jetzt auch über Twitter erhalten. Follow us!

Benutzerprofil von Eastler_dart

Öffentliches Profil

Allgemeine Informationen
Nutzergruppe:Mitglieder
E-Mail-Adresse:nicht öffentlich

Weitere Informationen sind nur für eingeloggte Benutzer sichtbar.


Statistische Informationen
Registriert seit:10.07.2008
Letzter Besuch:30.09.2008
Uploads:0
Projekte:1 [Auflisten]

Benutzerseite

nehmt das alles hier noch nicht zuuuu ernst, ich prüfe hier nur, wie meine Tags für Formatierungen bei Tutorials ankommen, ob ich Fehler hab etc. Wenn das hier alles seine Richtigkeit hat, wird es unter Tutorials eingestellt.


OPEN-GL Tutorial TEIL V - Texture

Einleitung

Sie haben doch bestimmt die vorigen vier Tutorialteile gelesen?
Wir bauen hier auf das dort Geschriebene auf.

Texturieren

Ja, so heißt das. Würde das irgendwie "Bilder aufkleben"
oder in der Art heißen, könnte man ja ahnen, was gemeint ist.

Nein, es wird Texturieren genannt. Auch wenn man fast
jedes x-beliebige Bild aufkleben kann.
Gedacht ist dabei daran, daß man ein Bild z.B. von einer
Holzoberfläche, also ein Bild der Maserung im Holz hat,
und dieses auf einen Quader klebt, sodaß dieser Quader dann
wie ein Balken aussieht.
Und solche Muster, die ein Material immitieren, die heißen
eigentlich "Textures".

Aber egal, heutzutage ist es ja so, daß wenn man kein
Englisch kann, man das Deutsch nicht mehr versteht.
Also reden auch wir übers Texturieren.

Es geht also darum, irgendwelche Objekte aus Dreiecken Vierecken
oder ähnlichem zu erstellen, und auf deren Oberfläche ein Bild
auflegen, sodaß diese Oberfläche ein etwas naturgetreueres Aussehen
erhält.

wie läuft das?

Am Anfang hat man eine Datei, in der das Bild drin steht.
Die liegt auf der Festplatte.
Am Ende muß dieses Bild auf der Grafikkarte stehen und
OpenGL muß dieses dann auf ein Objekt "kleben".

Wenn man jetzt OpenGL sagen könnte "ließ das Bild aus der
Datei ein", dann bräuchten wir hier kein FreeBasic, dann
würden wir alles unter OpenGL machen können.
OpenGL sitzt aber auf der Grafikkarte, und hat keinen blassen
Schimmer von Festplatten, USB-Sticks und anderer Peripherie.
An der Tastaturabfrage in unserem Beispiel können wir ja
erkennen, auch die Tastatur ist OpenGL unbekannt,
Eingaben des Anwenders muß FreeBasic überwachen.

Das Einzigste, was OpenGL außer der Grafikkarte noch kennt,
ist der Arbeitsspeicher des Computers, also der RAM.

Und damit ist klar, wie es laufen muß. FreeBasic kopiert
die Bilddatei in den Arbeitsspeicher, sagt OpenGL,
wo und in welcher Form das Bild im RAM steht, OpenGL
kopiert das dann auf die Grafikkarte und kann es damit
zum Texturieren verwenden.

Und wenn das jetzt jeweils einheitlich wäre, dann würde
ich Euch hier die zwei Befehle zeigen und wir hätten
Texturen auf der Grafikkarte.

Aber nein, frei nach Murphy, der Teufel steckt dann
im Detail.
Auf der Festplatte, im RAM und auf dem Grafikspeicher
gibt es jeweils verschiedene Arten, wie Bilder dort
abgespeichert sein können, und das ist das verwirrende
dabei:
Auf der Festplatte gibt es z.B. BMP-Dateien, TIFF-Dateien,
TGA-Dateien, diese Liste könnte ellenlang fortgeführt
werden.

Da FreeBasic unser Bild von der Festplatte in den RAM
kopiert, haben wir hier aus den vielen Möglichkeiten
nur diejenigen, die FreeBasic verwendet. Und das sind
im Grunde Bload-DIM-RAM-Bilder und BLOAD-ImageCreate-Bilder.

Auf der Grafikarte ist es recht einheitlich, Bilder,
welche OpenGL auf den Grafikspeicher holt, unterscheiden
sich nur durch die Farbtiefen und den Alphakanal,
einen grundsätzlichen Unterschied, wie das im Grafik-RAM
steht hab ich nicht gefunden.

So, und wenn ich nun auf jede Möglichkeit eingehen wollte,
wäre das Tutorial ellenlang und keiner würde durchblicken.

Also fange ich an, einzukreisen:

Bilder auf der Festplatte

Festplatte - wie ist das mit den vielen verschiedenen
Bildformaten? Wäre ja schön, wenn wir alle beherrschen würden.
Aber im Grunde üben wir hier ja nur. Somit würde uns zum
Ausprobieren ja ein einziges Format reichen.
Also nehm ich als Quelle von der Festplatte das einfachste
Format, die Bitmap-Datei (*.bmp). Dafür hat FreeBasic schon
ein paar Hilfsmittel drin, somit ist das nicht zuuu kompliziert.
Das Einzigste, was (noch?) nicht geht, sind komprimierte
Bitmap-Bilder. In unserem Beispiel werden wir nur unkomprimierte
Bitmap-Dateien verwenden können, die aber in allen verfügbaren
Farbtiefen.
Da Bitmap-Dateien recht groß sind, gibt es noch einen
Zusatztyp dazu, mit sogenannter RLE-Kompression.
Viele Bildbearbeitungsprogramme beherrschen dies, damit
erzeugt man wesentlich kleinere Dateien, als mit den
unkomprimierten Versionen.
Aber genau diese RLE-komprimierten Dateien können wir mit
FreeBasic's interer BLOAD-Routine NICHT einfach so
einlesen, deshalb gehe ich hier auch nicht näher darauf ein.
Merken Sie sich bitte, wenn Sie die Texturebilder selber
erstellen/Malen/Zeichnen, dann bitte als Bitmap OHNE
Komprimierung speichern.


Das Bild dann im Arbeitsspeicher

Arbeitsspeicher - wie sieht es damit aus?

Im Arbeitsspeicher kann man die Bilder auf beliebige
Arten abspeichern, die Möglichkeiten sind unbegrenzt.
Jedoch nützen uns nur die etwas, die wir Erstens
bearbeiten und Zweitens an OpenGL übergeben können.

Durch ettliche Versuche und Rumprobieren hab ich einen
Weg gefunden, der "sehr nah" an Freebasic liegt.
Da FreeBasic mittlerweile Ramspeicherreservierungen
am liebsten per ImageCreate durchführt, im (alten)
Beispiel, für OpenGL-Texturen jedoch noch per
DIM der Speicher reserviert wird, hatte ich nicht
sofort eine Lösung parat.
Aber soweit ich das bei mir testen konnte, gibts
einen Weg über ImageCreate.
Jedoch, wie auch im Beispiel in der "alten" Version
bleibt es nicht aus, manuell noch Farben zu vertauschen.

Also werden wir im Arbeitsspeicher per ImageCreate
den Bereich reservieren und per BLOAD das Bild
dort rein schaufeln.
Eine einzige Art für alles, das vereinfacht ungemein.

Bilder auf der Grafikkarte

Hier liegt der Unterschied im Grunde nur in der Farbtiefe,
also wieviel verschiedene Farben die Grafikkarte verwenden soll.
Klar, je mehr Farben, desto detailgenauer die Anzeige.

Bis vor einiger Zeit galt noch die Faustregel,
"geringere Farbtiefe = schneller Programmablauf".
Jedoch hat die moderne Technik diesen Umstand umgedreht.
Moderne Grafikkartenchips sind mittlerweile mit 32-Bit Farbtiefe
(=höchste Stufe) schneller, als mit geringeren Farbtiefen.
Und damit stehen wir wieder mal grübelnd da.

--Wenn unser Programm auf einer älteren Karte laufen wird,
dann sollte die Farbtiefe reduziert werden, dann wird das Ganze
schneller und auch, der bei älteren Karten kleine Speicher der
Karte ist nicht so schnell voll.
--Wenn mein Programm aber auf einer neueren Karte laufen wird,
dann sollte ich die Logik umdrehen, 32-Bit-Auflösung für schnelleren
Ablauf, Grafikspeicher ist in dem Fall ja bestimmt ausreichend
vorhanden.

Dabei gilt es auch zu bedenken, ein Programm, welches intensiv
OpenGL einsetzt, hat auf älteren Karten von vornherein verloren.
Auch wenn mit Farbtiefenreduzierung etwas an Leistung gewonnen
werden kann, bei großen texturierten Flächen, unter Drehung der
texturierten Objekte, stottert der Ablauf trotzdem.
Also sollten Sie auch die Belastung der von Ihnen programmierten
OpenGL-Funktionen einschätzen, sodaß Sie ggf. bereits damit erkennen,
"für alte Karten ist das nix -
-ich geb das Programm ab OpenGL-Version 1.3 frei"

(Versionsnummer beispielhaft ohne tieferen Sinn)

Aufgrund dieser Gedanken wird unsere Routine für die Übergabe
an OpenGL sich nach der Bittiefe des geöffneten OpenGL-Fensters
richten, und das sind genau zwei Möglichkeiten. 16-bit Farbtiefe
oder 32-bit Farbtiefe auf der Grafikkarte.

Summa Sumarum

Es erwarten Sie hier drei verschiedene Kapitel:


Im ersten Kapitel erst mal so einfach, wie es nur geht:
Hier werden wir erst mal nur mit unkomprimierten Bitmapdateien
als Quelle arbeiten.
Im Arbeitsspeicher legen wir diese in ImageCreate-Bereiche
mit 32-bit Farbtiefe ab.
Auf der Grafikkarte wird je nach der Farbtiefe des OpenGL-Fensters
mit 16 oder 32-Bit Tiefe das Bild gespeichert.

Im zweiten Kapitel bauen wir das Listing aus dem ersten
so aus, daß es für unkomprimierte Bitmapdateien voll
verwendbar ist. Viele der eventuell auftretenden Fehler werden
abgefangen, der darin entstehenden Function brauchen wir
nur einen Dateinamen zu übergeben, den Rest macht die Function.

Im dritten und letzten Kapitel holen wir uns Hife von der
Library FreeImage, um die Dateien von der Festplatte in
den Arbeitsspeicher zu kopieren.
Und damit sprengen wir die Einschränkung der beiden Kapitel
zuvor, nur mit Bitmap-Dateien arbeiten zu können.
Durch die FreeImage-Library können z.B. Png oder Jpg Dateien
ebenfalls verwendet werden.

[seitenwechsel]

Erst mal so einfach, wie es geht

Um fürs Erste nicht komplizierter als nötig zu werden, gehen
wir mal von festen Vorgaben aus:

Nehmen wir ein Beispielbild, in der Auflösung 128x128 Pixel,
welches auf unserer Festplatte im unkomprimierten Bitmapformat
steht.
Dafür hab ich hier das Bild mauer_128.bmp,
mauer_128.bmp
welches Sie per Rechtsklick und "Speichern unter"
auf Ihre Festplatte speichern können.
Das Bild sollten Sie in dasjenige Verzeichnis speichern,
in dem später die fertige Exe-Datei, die wir hier erarbeiten,
stehen wird.

So, also was haben wir als Grunddaten:
Eine Bilddatei, die Bildgröße ist 128x128 Pixel, die Farbtiefe
ist in dem Beispielbild 24-bit, die Datei ist nicht komprimiert.

die Datei in den Ram

Was wir brauchen, ist ein Ramspeicherbereich, in den das
Bild aus der Datei reinpaßt. Und da wir für unser erstes
Beispiel wissen, daß in der Datei ein bild mit 128x128 Pixel
liegt, können wir hier vorläufig mit festen Werten Arbeiten.

Da wir später diesen Rambereich an OpenGL so übergeben wollen,
daß bereits OpenGL V1.0 dies beherrscht, müssen wir uns auf
32-bit Farbtiefe im Ram festlegen. Der BLOAD-Befehl ist so clever,
daß er beim Einlesen einer Bitmap-Datei in einen RAM-BEREICH
die Daten in dasjenige Format umwandelt, mit dem ImageCreate den
Bereich initialisiert hat, bei uns also 32-bit.
Die Bildqualität liegt dabei bei 24-bit, die restlichen 8-bit
sind für den Alpha-Kanal reserviert, der ist in BMP-Dateien
aber normalerweise gar nicht vorhanden, sodaß dieser Wert
im Ramspeicher nur initialisiert wird.

Also egal, ob Sie nun eine Bmp-Datei in 1-bit, 4-bit, 8-bit
oder in 24-bit haben, wenn Sie diese per BLOAD in den mit
ImageCreate auf 32-bit reservierten Bereich laden, steht
sie dort als 24-bit-Bild.

Und somit sind die Fakten für ImageCreate jetzt vollständig:
Bild-Breite = 128pixel Bild-Hoehe= 128pixel Farb-Tiefe = 32bits

ImageCreate gibt uns dann einen Pointer/Zeiger auf den
Datenbereich im RAM zurück, den wir in einer Variablen
festhalten müssen. Also dimmen wir die Variable "DateiImRamPtr"
DIM AS BYTE PTR DateiImRamPtr
Dazu wäre zu sagen, laut FB-Hilfe sollte die Variable
als "ANY PTR" gedimmt werden. Für das weitere Bearbeiten
des RAM-Bildes bräuchten wir aber ein Byte-Pointer. So
wie ich das getestet habe, läuft das auch so, fehlerfrei.

Da wir nun eine Variable für den Zeiger haben, können wir
den Ram anfordern und mit schwarz(0) füllen:
DateiImRamPtr = IMAGECREATE(128, 128, 0, 32)

Bleibt nur noch, das Bild von der Festplatte in diesen
Rambereich reinkopieren:
BLOAD("mauer_128.bmp", DateiImRamPtr)
Bload will nur den Dateinamen und den Zeiger auf den RAM.
Am Dateinamen (.bmp)erkennt BLOAD, daß er eine Bitmap
einlesen soll, im Rambereich steht von ImageCreate schon
drin, daß das Bitmap-Bild mit 32-bit Farbtiefe in den
Ram geschrieben werden soll.

Daraus ergibt sich für das Kopieren der Festplattendatei
in den Ram folgendes Listing:

DIM AS BYTE PTR DateiImRamPtr
DateiImRamPtr = IMAGECREATE(128, 128, 0, 32)
BLOAD("mauer_128.bmp", DateiImRamPtr)

Bild im RAM für OpenGL anpassen

Jetzt wirds ein bisschen kniffelig.
Bis jetzt hatte ich immer behauptet, OpenGL würde
solche BMP-Bilder freudig annehmen. Ganz so einfach
ist es jedoch nicht. So eine Bitmap im Ram hat
nämlich die Werte für Rot und Blau vertauscht,
gegenüber dem, was OpenGL erwartet.

Das Problem liegt aber nicht bei FreeBasic,
sondern am Format der 24-bittigen Bitmaps.
Laut Dateibeschreibungen sind nur in dieser
Farbtiefe bereits in der Datei die Farben
vertauscht. In vielen anderen Fällen ist das
von Vorteil, bei uns hier mit FreeBasic und
OpenGL ist es leider eine Extrahürde.


Und auch noch zusätzlich steht das Problem an,
daß für FreeBasic es von Vorteil ist, wenn
das Bild beim Laden per BLOAD in den Ram
senkrecht gespiegelt wird. Tja, für FreeBasic
mag das ja ein Vorteil sein, bei uns stört das.
Jedoch nur, wenn die Ausrichtung eines Bildes
einen tieferen Sinn hat, z.B. Schriften drin
sind oder so.
Erst mal geh ich davon aus, daß wir nur einfache
Texturen verwenden, also Holzmaserungen, Mauer-
bilder oder so, und bei denen sehe ich keine
Notwendigkeit, diese grundsätzlich zurück
zu spiegeln.
Für das zurück Spiegeln müßten wir einen zweiten
Bildpuffer anlegen, was ich aber hier der zur
Vereinfachung mal noch nicht machen will.
Wir merken uns: mit dieser Routine wird an unserem
Bild Oben und Unten vertauscht!

Also (erst mal) nur das Farbproblem lösen.
Das bedeutet, wir müssen jetzt das Bild Pixel
für Pixel durchgehen, und die Werte von Rot
und Blau austauschen.

Kann man das nicht gleich beim Datei-Einlesen machen?
klar, das ginge irgendwie auch, wäre aber besonders
langsam, von Festplatte einzeln ein Pixel nach dem
anderen zu holen. Und, wir würden damit darauf
verzichten, daß FreeBasic die 4-Bit/8-Bit-Bilder
uns komfortabel automatisch auf 24=32-Bit umsetzt.
Per BLOAD ins RAM, Pixel für Pixel Farben tauschen,
das ist der schnellste Weg und auch 4 und 8-bit-Bilder
können damit ebenfalls als Textur verwendet werden.

Eigentlich sollte es jetzt ein Einfaches sein, per
POINT die Farbwerte eines Pixels in eine Variable
zu holen, die Farbwerte Rot und Blau in der Variable
austauschen und per PSET wieder zurück schreiben.
Jedoch mußte ich feststellen, daß es Konstellationen
gibt, bei denen dabei Fehler entstehen.
Z.B. wenn wir das OpenGL-Fenster 16-bittig öffnen,
den Ramspeicher für das Bild aber 32-bittig erstellen,
dann gehen POINT und PSET davon aus, das Bild würde
16-bittig wie das OpenGL-Fenster im Ram stehen,
obwohl im Rambereich des Bildes klar drin steht,
daß es 32-bittig ist.

Somit fallen POINT und PSET aus, der sicherste Weg,
den ich gefunden hat, ist, die einzelnen Bytes der
Farbwerte im RAM einzeln, direkt anzusprechen.
Nun hoffe ich, daß Sie sich damit schon auskennen,
damit ich das nicht allzu ausschweifend erklären muß.
Notfalls finden Sie weitere Erklärungen dazu in der
FreeBasic-Hilfe oder im Forum.
Also nur kurz, RAM-Speicher direkt ansprechen:
Für unser Beispiel braucht man dazu eine Variable, die
nicht Werte sondern RAM-Adressen speichert, sogenannte
POINTER, z.B. DIM AS BYTE PTR ByteZeiger.
Würden wir z.B. nun PRINT ByteZeiger schreiben,
erhielten wir eine lange Zahl, welche die Adresse im
Ramspeicher darstellt. Würden wir den Inhalt dieser
RAM-Speicheradresse anzeigen wollen, müßten wir einfach
einen Stern vor den Variablennamen setzen:
   PRINT *ByteZeiger
Damit holt FreeBasic an der Speicheradresse den Inhalt
des dort stehenden Bytes (Byte-Pointer!).
In unserem Fall müssen wir jedoch noch dereferenzieren,
sowas wie bei der Post:
   "Drei Häuser weiter als Hauptstrasse 100"
und das geschieht mit den eckigen Klammern:
   PRINT ByteZeiger[3]
Man beachte hierbei: durch die eckigen Klammern geht
FreeBasic bereits davon aus, der Inhalt der Ram-Adresse
ist gemeint, nicht der Adressenwert - BEI VERWENDEN VON
ECKIGEN KLAMMERN KEIN STERN DAVOR SETZEN.
Als letzte Info noch das Verändern des Inhaltes der
Pointervariable ByteZeiger:
Da in einer Pointervariable Adresswerte(Ganzzahlen)
stehen, darf man auch direkt andere Zahlen reinsetzen.
Somit kann man z.B. mit ByteZeiger = ByteZeiger + 4
dafür sorgen, daß beim nächsten Verwenden dieser
Pointervariable im RAM auf das vier Bytes weiter hinten
liegende Byte zugegriffen wird. So würden:
   PRINT ByteZeiger[4]
und
   ByteZeiger=ByteZeiger+4 : PRINT *ByteZeiger
genau das Gleiche bewirken, im zweiten Beispiel steht
dann aber die um 4 erhöhte Adresse in der Pointervariablen
gegenüber dem ersten Beispiel


Also heißt es, mit welchem Konstrukt können wir
ein Pixel nach dem Anderen holen.
Klar, FOR-NEXT ist die Devise. Dabei verschachteln
wir zwei solcher Schleifen ineinander, sodaß wir
mit mit den Angaben von Breite in Pixel und Hoehe in Pixel
arbeiten können:
FOR ZeilenZaehler = 0 TO 127
FOR PunktZaehler = 0 TO 127
[hier die Farbumsetzungen]
NEXT PunktZaehler
NEXT ZeilenZaehler


Innerhalb der Schleife müssen wir dann von jedem Pixel
den Farbwert holen. Wie jedoch schon gesagt, steht an
der Adresse, die ImageCreate zurückgibt, vor den eigentlichen
Pixel-Daten noch andere Infos, wie Farbtiefe, Breite
Größe etc.
Eigentlich könnten wir nun ermitteln, wie lang dieser
"InfoVorspann" ist, und entsprechend soviele Bytes
weiter hinten anfangen zu lesen.
Ab FB Ver 0.20 gibt es den Befehl ImageInfo, mit dem
man direkt die Adresse der Speicherstelle des ersten
Pixels des Bildes kriegen könnte. Dazu müßten wir
aber viele Variablen etc anlegen, um dieses ImageInfo
durzuführen. Gleichzeitig würde unsere Routine "nur"
ab FB-Version 0.20 laufen.
Deshalb hab ich einen kürzeren Weg gesucht. Im Grunde
ist es einfach, im Ram stehen Vorspann und Pixeldaten
hintereinander weg.
ImageCreate-Adresse + Länge des Vorspanns wäre die
Adresse für das erste Pixel des Bildes.
Und dieser Vorspann hat FreeBasic als Datentyp
hinterlegt, der heißt FB.IMAGE.
Die Länge eines Datentypes erhält man mit dem Befehl
LEN(). Somit liefert LEN(FB.IMAGE) die
Länge des Vorspanns. Wenn man nun diesen Wert zu der
Adresse von ImageCreate dazuzählt, hat man die
RAM-Adresse des ersten Pixels.
Wir dürfen die AdressVariable von ImageCreate jedoch
nicht verändern, um später den RAM-Speicher
auch wieder freigeben zu können, brauchen wir diese
Adresse noch.
Also Dimmen wir eine Zweite Byte-Pointer-Variable,
in die wir die Adresse der Pixeldaten reinsetzen:
DIM AS BYTE PTR SpBmpPixelZeiger
und belegen diese Variable mit der Adresse des
ersten Bildpixels:
SpBmpPixelZeiger = DateiImRamPtr + Len(FB.IMAGE)
(natürlich dimmen und belegen wir SpBmpPixelZeiger
noch vor dem Beginn der Schleifen)

Innerhalb der Schleifen wollen wir ja Rot und Blau
vertauschen, dazu brauchen wir Variablen, um die
beiden Werte erst mal zwischenzuspeichern.
DIM AS UINTEGER RotWert, BlauWert
Da die einzelnen Farbwerte die Werte von 0 bis 255
haben können, passen sie super in UINTEGER-Variablen.

So, Adresse der Pixel ist da, Zwischenspeicher
der Farbwerte ist da, somit können wir jetzt die
notwendigen Funktionen in die Schleifen setzen.
Einfach das Byte an Adresse SpBmpPixelZeiger für
den Rot-Wert einlesen, und das 2 Bytes dahinter
liegende für den Blauwert:
BlauWert = SpBmpPixelZeiger[0]
RotWert = SpBmpPixelZeiger[2]

und nun vertauscht zurückschreiben:
SpBmpPixelZeiger[0] = RotWert
SpBmpPixelZeiger[2] = BlauWert

Nun noch für den nächsten FOR-NEXT-Schleifendurchlauf
die Adresse der Pixeldaten um 4(bytes) erhöhen,
damit danach auf das zweite.... Pixel zugegriffen wird
(Sie erinnern sich? 32-bit speicherfarbtiefe = 4 bytes)
SpBmpPixelZeiger = SpBmpPixelZeiger+4

Damit hätten wir für das Farbe-Umsetzen folgendes
(Teil-)listing:

DIM AS BYTE PTR SpBmpPixelZeiger
DIM AS UINTEGER RotWert, BlauWert
DIM AS UINTEGER ZeilenZaehler, PunktZaehler
SpBmpPixelZeiger = DateiImRamPtr + Len(FB.IMAGE)
FOR ZeilenZaehler = 0 TO 127
   FOR PunktZaehler = 0 TO 127
      BlauWert = SpBmpPixelZeiger[0]        :'aus Ram das Byte mit dem Blauwert des grad zu bearbeitenden Pixels holen
      RotWert  = SpBmpPixelZeiger[2]        :'aus Ram das Byte mit dem Rotwert des grad zu bearbeitenden Pixels holen
      SpBmpPixelZeiger[0] = RotWert         :'den Rotwert an die Stelle des Blauwertes im RAM schreiben
      SpBmpPixelZeiger[2] = BlauWert        :'den Blauwert an die Stelle des Rotwertes im RAM schreiben
      SpBmpPixelZeiger = SpBmpPixelZeiger+4 :'Ram-Adresse um 4 bytes erhöhen = Start Pixeldaten des nächsten Pixels
   NEXT PunktZaehler
NEXT ZeilenZaehler

aufbereitetes Bild im Ram an OpenGL uebergeben

Nun haben wir die Daten so, wie wir sie OpenGL übergeben können.
Also ran ans Werk.

Als Erstes müssen wir OpenGL um einen Namen für die im
Grafikartenspeicher abzulegende Textur bitten, mit
glGenTextures 1, @TexturNummer
erledigen wir dies, wobei die Variable TexturNummer zuvor
als UINTEGER gedimmt sein sollte. Man beachte, daß hier
(das @-Zeichen davor) nicht die Variable, sondern deren
Adresse im RAM zu übergeben ist!
OpenGL schreibt in diese Variable "TexturNummer" den
Namen der Textur, für die nun "Platz" auf der Grafikkarte
eingerichtet ist.
Dabei spreche ich von TexturNAMEN, weil OpenGL (vorerst?)
zwar nur Nummern zurück gibt, diese jedoch bei mehreren Texturen
nicht zwingend fortlaufend sind, sondern auch wahllos
irgendwelche Zahlen sein können.

Nachdem nun ein Platz gerichtet ist, muß dieser unbedingt
gleich angesprochen werden, dies geschieht mit:
glBindTexture GL_TEXTURE_2D, TexturNummer

Damit haben wir alles, was wir für die Übergabe des Bildes
an OpenGL brauchen.
Auf der Grafikarte ist eine Textur im Index angelegt, wir
brauchen nur noch die Bilddaten zu übergeben.
Das geschieht für unsere Zwecke per glTexImage2D
wir wollen ja eine zweidimensionale Textur (Bild mit Höhe und Breite).
Als Parameter verlangt glTexImage2D folgende:
1: TextureArt = für uns immer GL_TEXTURE_2D
2: DetailMenge = für uns erst mal 0, hier könnte man mehrere
Texturbilder anlegen, die je nach Entfernung Kamera/Objekt
verwendet werden (= für den Anfang zu kompliziert)
3: Art, wie die Daten auf der Grafikkarte zu speichern sind.
Für unser erestes Beispiel hier, nehmen wir GL_RGB, daß heißt
daß wir RotWerte, GrünWerte und BlauWerte ablegen wollen,
(noch) keine Alpha-Werte. Mit diese Einstellung läuft unser
Beispiel in OpenGL-Fenstern von 15/16/24 und auch 32-bit Farbtiefe.
4:Breite des Bildes in Pixel - in unserem Beispiel also 128
5:Hoehe des Bildes in Pixel - in unserem Beispiel 128
6: Rahmen um die Textur? (0=keiner, 1=ja) - wir wollen keinen Rahmen = 0
7: Format der Farbangaben im RamSpeicher, bei uns GL_RGBA = 1Byte für Rot, eins für Grün, eins für Blau und noch eins für Alpha (=32-bit)
8: Datentyp im Ram, wir haben GL_UNSIGNED_BYTES dort stehen (Bytes die 1-255 als Wert aufnehmen können)
9: Adresse im RAM der Daten des ersten Pixels - ImageCreateAdresse + Vorspann

In der Zusammenfassung des gerade Gesagten sieht die
Übergabe des Bildes an OpenGL so aus:
glTexImage2D GL_TEXTURE_2D, 0, GL_RGB, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, DateiImRamPtr+Len(FB.IMAGE)

zum Schluß sollten wir noch an den Filtern Texture_MIN und
Texture_MAG die Parameter setzen. Hier entscheiden wir darüber,
wie OpenGL mit den Texturen umgeht, wenn wir mit der Kamera
gaaanz weit weg vom Objekt sind, die texturierte Fläche also
recht klein ausfällt, und auch, wenn wir gaaanz nah dran
sind, die texturrierte Fläche also auf unsere Nase klebt.
Hier gilt, GL_Linear gibt ein schöneres Bild,
GL_Nearest spart Rechenpower, eine Textur, welche durch
GL_Nearest gefiltert wird bei zu nah dran oder zu weit weg
ungenau, die (errechneten)Pixel werden manchmal zu Blöcken.
Sie können auch für GL_TEXTURE_MIN_FILTER (wenn die Kamera weit weg ist)
einen anderen wert als für GL_TEXTURE_MAG_FILTER(Kamera nah dran)
angeben, Ausprobieren ist gefragt. Mit GL_LINEAR auf beiden
Filtern ist die Textur immer schön weich gezeichnet.
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR

So, nachdem wir das Bild auch auf der Grafikarte gespeichert
haben können wir den reservierten RAM-Speicher wieder frei geben.
mit ImageDestroy(DateiImRamPtr) wird das Bild im RAM wieder
gelöscht, der Arbeitsspeicher ist wieder zugänglich.

Für den Teil "Bild an OpenGL" ergibt sich somit folgendes Listing:

DIM AS UINTEGER TexturNummer
glGenTextures 1, @TexturNummer
glBindTexture GL_TEXTURE_2D, TexturNummer
glTexImage2D GL_TEXTURE_2D, 0, GL_RGB, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, DateiImRamPtr+Len(FB.IMAGE)
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR
ImageDestroy(DateiImRamPtr)

Bild bei OpenGL als Texture verwenden

So, OpenGL hat nun unser Bild, wir können es mit dem "Namen,
der in unserer Variable TexturNummer liegt, verwenden.

Dazu müssen wir erst mal Texturieren erlauben:
glEnable GL_TEXTURE_2D
Danach noch eine Einstellung machen, daß OpenGL unser Bild
ohne Rücksicht auf Objektfarbe etc einfach draufklebt,
soll heißen, wie ein Abziehbild draufkleben, nix schimmert
durch oder sonstige Extras:
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)

Nun aktiviert man ein(eines der vielen? an OpenGL übergebenen) Bild
bei OpenGL als Texturebild, alles, was nach diesem Befehl kommt und
Texturen verwendet, nimmt dann dieses Bild dazu.
Bei mehreren Texturen muß man halt eins Aktivieren, das dazugehörige
Objekt erstellen, ein anderes aktivieren und danach das andere Objekt
erstellen.
Also sagen wir OpenGL, das es "unser" Bild künftig für Texturen verwenden
soll (in TexturNummer liegt der Name unseres Bildes bei OpenGL):
glBindTexture GL_TEXTURE_2D, TexturNummer

So, jetzt nur noch das Texturieren selber.
Ein Objekt erstellen, und dem eine Textur draufkleben bedeutet,
für jeden Punkt des Objektes angeben, wo dieser Punkt auf dem
Texturbild liegen soll.
Damit kann man Texturbilder verzerren, verziehen oder zurecht
biegen.
Klar, wir wollen hier immer noch so einfach wie möglich vorgehen,
wir erstellen also ein Viereck.
Solch ein Viereck besteht ja aus vier Punkten, die wir deffinieren
müssen. Zu jedem Punkt sagen wir dann gleich mit dazu, von welcher
Stelle im Texturbild das Pixel an diesem Punkt genommen wird.
Solche Angaben müssen vor der eigentlichen Punkt-Deffinition
im 3D-Raum erfolgen.

Hier mal eine Übersicht:
Ogl_tut_5_texturKoordinaten
Hier hab ich demonstrativ das Viereck in einer Größe
von 2 Einheiten hoch und 2 Einheiten breit angelegt,
um zu zeigen, daß die Werte des Vierecks nichts mit
den Werten im Texturbild zu tun haben!

Im Texturbild wird mittels Kommazahlen ein Punkt im Bild
per X-Pos, Y-Pos adressiert.
Dabei gilt, der Wert 0(null) = auf X ganz links, auf y ganz unten.
der Wert 1 auf X ist ganz rechts, auf Y ganz oben.
Es spielt keine Rolle, in welcher Pixelgröße (bei uns 128x128)
das Texturbild im Speicher steht.
Ob 64x64 oder gar 256,256, die Pos 1,1 ist immer ganz rechts und ganz oben,
die Pos 0,0 ganz links und ganz unten.

Natürlich können Zwischenwerte angegeben werden,
so z.B. der Bildmittelpunkt ist 0.5, 0.5,
also 0.5 in X-Richtung und 0.5 in Y-Richtung.
Aber auch z.B. 0.256 oder 0.3785961 sind möglich.
Würden wir z.B. in unserem Bild in X-Richtung das
67.Pixel brauchen, da wäre die Pos in X-Richtung
1.0/128*67 = 0.5234375.

Der Vollständigkeit halber muß ich noch erwähnen,
daß auch Werte größer 1.0 "richtige" Werte sind, sie stehen dafür,
daß OpenGL das Quellbild mehrfach neben- / untereinander
auf das Objekt klebt, z.B. mit den TexKoordinaten 3.0, 3.0
würde das Quellbild 3x neben und 3x untereinander zusammen-
geklebt werden, also 9 mal für die Fläche verwendet.

Aber wir machen es uns ja einfach, einfach die Ecken des Bildes
entsprechend auf die Ecken des Vierecks setzen.
Und dazu verwenden wir den Befehl von OpenGL:
glTexCoord mit einer 2 für zwei Koordinatenangaben(x/y)
und einem d für doppelte Wertgenauigkeit.
Also glTexCoord2d. Dahinter einfach die X-Pos und
dann die Y-Pos im Bild als Kommazahl.
Und dieser Punkt im Bild wird dem nachfolgenden 3D-Punkt zugeordnet.
Also: zuerst ein Pixel im Bild festlegen, danach den Punkt des
Objekts deffinieren. Danach das nächste Pixel im Bild benennen und
den nächsten 3D-Punkt des Objektes angeben usw.
Damit wir die Zuordnung eines Texturpunktes auf einen Punkt unseres
3D-Objektes besser erkennen können, schreiben wir die beiden
Befehle in eine einzige Zeile.
Sie wissen ja, mehrere Befehle in einer Zeile werden durch
Doppelpunkt voneinander getrennt.

Dem entsprechend und anhand des Übersichtsbildes vorhin,
ergibt sich folgende Objektdeffinition für unser Viereck:

glBegin GL_QUADS :'Beginn Beschreibungsliste Objekt Viereck
glTexCoord2d 0.0, 1.0 : glVertex3f -1.0, 1.0, -4.0 :'die linke obere Ecke des Rechtecks
glTexCoord2d 0.0, 0.0 : glVertex3f -1.0, -1.0, -4.0 :'die linke untere Ecke des Rechtecks
glTexCoord2d 1.0, 0.0 : glVertex3f +1.0, -1.0, -4.0 :'die rechte untere Ecke des Rechtecks
glTexCoord2d 1.0, 1.0 : glVertex3f +1.0, 1.0, -4.0 :'die rechte obere Ecke des Rechtecks
glEnd :'Fertig mit dem Objekt


Und jetzt alles zusammen

Ja, wir haben alles. Eine Code-Schnipserl, um Bmp-Bilddateien
von der Festplatte in den Ram zu kopieren, einen umd dieses
Ram-Bild farblich richtig zu stellen, und den dritten, um
dieses korrigierte Bild im Ram an OpenGL zu geben.
Dann hätten wir auch die Routine, um die Textur auf einem
Viereck zu verwenden.

Das ist zwar alles (noch) sehr speziell darauf zugeschnitten,
daß ein Bild mauer_128.bmp unkomprimiert und in
der Größe 128x128 Pixel in genau dem gleichen Verzeichnis
liegt, wie unser kompiliertes Programm, aber es würde
funktionieren. :-)

Wir wollen das doch mal "live" sehen, oder?
Also nehmen wir unser Listing aus Tutorial 4, und setzen die
hier erarbeiteten Code-Abschnitte an die richtigen Stellen:

'REM Tutorial 4 - OpenGL-Tutorial von Eastler
'-------------------------
'DIMs
'-------------------------
DIM SHARED AS STRING Tastendruck
DIM SHARED AS STRING Tlinks, Trechts, Tvor, Tzurueck, TCtrlVor, TCtrlZurueck, TCtrlLinks, TCtrlRechts
Tlinks       = CHR(255) & CHR( 75)  :' beim Drücken der Taste CursorLinks gibt die Tastatur CHR(255) & CHR( 75) zurück
Trechts      = CHR(255) & CHR( 77)  :' CursorRechts
Tvor         = CHR(255) & CHR( 72)  :' CursorHoch
Tzurueck     = CHR(255) & CHR( 80)  :' CursorRunter
TCtrlVor     = CHR(255) & CHR(141)  :'Ctrl oder STRG zusammen mit CursorHoch
TCtrlZurueck = CHR(255) & CHR(145)  :'Ctrl oder STRG zusammen mit CursorRunter
TCtrlLinks   = CHR(255) & CHR(115)  :'<--------CURSOR NACH LINKS-Variable mit Wert belegen
TCtrlRechts  = CHR(255) & CHR(116)  :'<--------CURSOR NACH RECHTS-Variable mit Wert belegenDIM AS SINGLE XRichtg, YRichtg, ZRichtg
DIM AS SINGLE XRichtg, YRichtg, ZRichtg
DIM AS SINGLE PyraXDrehw, PyraYDrehw, PyraZDrehw :'<------------------------ für Pyramide per Tasten drehen
DIM AS SINGLE WeltDrehX, WeltDrehY, WeltDrehZ
DIM AS INTEGER ConNr                :' Dateinummerspeicher beim Consolefenster öffnen
DIM SHARED AS DOUBLE Pi
Pi = 3.14159265358979323846
'-------------------------
'Includes
'-------------------------
#include "fbgfx.bi"
#include once "GL/gl.bi"
#include once "GL/glu.bi"
#include once "vbcompat.bi" '<--------------um Zahlen per "format()" formatieren zu können

'-------------------------
'Declarationen
'-------------------------
DECLARE SUB Koordinatensystem(was AS STRING, TxtPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
DECLARE SUB Schachbrettboden(was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
DECLARE SUB Pyramide (was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)

'-------------------------
' das Fenster öffnen
'-------------------------
screen 19, 32, , 2

'-------------------------
' Open-GL Init
'-------------------------
glViewport 0, 0, 800, 600                      ' den Current Viewport auf eine Ausgangsposition setzen
glMatrixMode GL_PROJECTION                     ' Den Matrix-Modus Projection wählen
glLoadIdentity                                 ' Diesen Modus auf Anfangswerte setzen
gluPerspective 45.0, 800.0/600.0, 0.1, 100.0   ' Grundeinstellungen des Anezeigefensters festlegen
glMatrixMode GL_MODELVIEW                      ' Auf den Matrix-Modus Modelview schalten
glLoadIdentity                                 ' und auch diesen auf Anfangswerte setzen
'-----------------------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXX HIER DAS CODESTÜCK Von Festplatte in den RAM XXXXXXXXXXXXXXXX
'-----------------------------------------------------------------------------------
DIM AS BYTE PTR DateiImRamPtr
DateiImRamPtr = IMAGECREATE(128, 128, 0, 32)
BLOAD("mauer_128.bmp", DateiImRamPtr)
'---------------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXX GLEICH WEITER MIT FARBEN AUSTAUSCHEN XXXXXXXXXXXXXXXX
'---------------------------------------------------------------------------
DIM AS BYTE PTR SpBmpPixelZeiger
DIM AS UINTEGER RotWert, BlauWert
DIM AS UINTEGER ZeilenZaehler, PunktZaehler
SpBmpPixelZeiger = DateiImRamPtr + Len(FB.IMAGE)
FOR ZeilenZaehler = 0 TO 127
   FOR PunktZaehler = 0 TO 127
      BlauWert = SpBmpPixelZeiger[0]        :'aus Ram das Byte mit dem Blauwert des grad zu bearbeitenden Pixels holen
      RotWert  = SpBmpPixelZeiger[2]        :'aus Ram das Byte mit dem Rotwert des grad zu bearbeitenden Pixels holen
      SpBmpPixelZeiger[0] = RotWert         :'den Rotwert an die Stelle des Blauwertes im RAM schreiben
      SpBmpPixelZeiger[2] = BlauWert        :'den Blauwert an die Stelle des Rotwertes im RAM schreiben
      SpBmpPixelZeiger = SpBmpPixelZeiger+4 :'Ram-Adresse um 4 bytes erhöhen = Start Pixeldaten des nächsten Pixels
   NEXT PunktZaehler
NEXT ZeilenZaehler
'---------------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXX UND RAM-BILD AN OPENGL ÜBERGEBRN XXXXXXXXXXXXXXXX
'---------------------------------------------------------------------------
DIM AS UINTEGER TexturNummer
glGenTextures 1, @TexturNummer
glBindTexture GL_TEXTURE_2D, TexturNummer
glTexImage2D GL_TEXTURE_2D, 0, GL_RGB, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, DateiImRamPtr+Len(FB.IMAGE)
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR
ImageDestroy(DateiImRamPtr)
'---------------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
'---------------------------------------------------------------------------
glClearColor 0.5, 0.5, 0.50, 0.0               ' Setze Farbe für löschen auf Mittelgrau
glClearDepth 1.0                               ' Depth-Buffer Löschen erlauben
glEnable GL_DEPTH_TEST                         ' den Tiefentest GL_DEPTH_TEST einschalten
glClear GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT  'Tiefen- und Farbpufferbits löschen
'---------------------------
'HAUPTTEIL
'---------------------------
Schachbrettboden("SetzKantenLaenge", "", 0.5,  0,    0)
Schachbrettboden("SetzQuadsZahl",    "", 24,   24,   0)     :'6 Einheiten +und- = 12Einheiten Koordiantensystem, bei 0.5 Quadgröße 24 Stück
Schachbrettboden("SetzFarbe1",       "", 0,    0,    0.5)   :'erste Farbe dunkleres blau
Schachbrettboden("SetzFarbe2",       "", 0.25, 0.25, 0.25)  :'erste Farbe dunkles grau
Schachbrettboden("SetzStartPos",     "", -6,   -6,   -1)     :'ganz vorne ganz links beginnen, Boden auf Hoehe(3.Para)-1 verlegen(Y)

Pyramide("SetzLaengen" , "", 1, 1,  1)
Pyramide("SetzFarbe1"  , "", 1, 0, 0)
Pyramide("SetzFarbe2"  , "", 0, 1, 0)
Pyramide("SetzFarbe3"  , "", 0, 0, 1)
Pyramide("SetzFarbe4"  , "", 1, 1, 0)
Pyramide("SetzFarbe5"  , "", 1, 0, 1)

DO UNTIL Tastendruck = CHR(27)                         :'die Schleife solange immer wiederholen, bis in der Variablen Tastendruck die Esc-Taste (chr(27) steht
   Tastendruck = INKEY                                 :'Jeder Tastendruck wird sofort in die Variable Tastendruck gespeichert
   '---------------------------
   'ProgrammSchleife
   '---------------------------

   'JE NACH TASTENDRUCK DEN ENTSPRECHENDEN POSITIONSWERT VERÄNDERN
   SELECT CASE Tastendruck
      CASE "K", "k"
         Koordinatensystem("AnAus", "", 0, 0, 0) :' Schalter umstellen, falls An ist, auf Aus, sonst auf An
      CASE TCtrlrechts                :'Falls die Tasten "Cursor nach rechts" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         XRichtg = XRichtg - COS((WeltDrehY * Pi) / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg - SIN((WeltDrehY * Pi) / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TCtrllinks                 :'Falls die Tasten "Cursor nach rechts" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         XRichtg = XRichtg + COS((WeltDrehY * Pi) / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg + SIN((WeltDrehY * Pi) / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TCtrlzurueck               :'Falls die Tasten "Cursor unten" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         YRichtg = YRichtg + 0.04 :'HOCH und Runter bleibt unberührt von der Y-Drehung
      CASE TCtrlvor                   :'Falls die Tasten "Cursor hoch" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         YRichtg = YRichtg - 0.04 :'HOCH und Runter bleibt unberührt von der Y-Drehung
      CASE TZurueck                   :'Falls die Taste "Cursor runter" gedrückt wurde
         XRichtg = XRichtg + COS((WeltDrehY-90) * Pi / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg + SIN((WeltDrehY-90) * Pi / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TVor                       :'Falls die Taste "Cursor hoch" gedrückt wurde
         XRichtg = XRichtg + COS((WeltDrehY+90) * Pi / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg + SIN((WeltDrehY+90) * Pi / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TLinks                      :'Falls die Taste "Cursor links" gedrückt wurde
         WeltDrehY = WeltDrehY - 1
      CASE TRechts                     :'Falls die Taste "Cursor rechts" gedrückt wurde
         WeltDrehY = WeltDrehY + 1
      CASE "x"
         PyraXDrehw=PyraXDrehw+1
      CASE "X"
         PyraXDrehw=PyraXDrehw-1
      CASE "y"
         PyraYDrehw=PyraYDrehw+12
      CASE "Y"
         PyraYDrehw=PyraYDrehw-12
      CASE "z"
         PyraZDrehw=PyraZDrehw+1
      CASE "Z"
         PyraZDrehw=PyraZDrehw-1
      CASE "i", "I"                       :'<------------------------
         ConNr = FREEFILE                 :'nächste freie Dateinummer für öffnen von FreeBasic holen und in ConNr merken
         open "con" FOR OUTPUT AS #ConNr  :'und nun die Konsole als Datei unter Nr ConNr öffnen
            PRINT #ConNr, "PyraXDrehw:" & format(PyraXDrehw,"0.00") & "  PyraYDrehw:" & format(PyraYDrehw,"0.00") & "  PyraZDrehw:" & format(PyraZDrehw,"0.00")
            PRINT #ConNr, "XRichtg:" & format(XRichtg,"0.00") & "  YRichtg:" & format(YRichtg,"0.00") & "  ZRichtg:" & format(ZRichtg,"0.00")
         CLOSE #ConNr
   END SELECT   'PRÜFEN, DASS DIE POSITIONSWERTE IN ALLEN DREI RICHTUNGEN ZWISCHEN -3 und +3 BLEIBEN
   IF XRichtg >  6 THEN XRichtg =  6           :'falls zu weit rechts, bei  6 festnageln
   IF XRichtg < -6 THEN XRichtg = -6           :'falls zu weit links,  bei -6 festnageln
   IF YRichtg >  6 THEN YRichtg =  6           :'falls zu weit hoch,   bei  6festnageln
   IF YRichtg < -6 THEN YRichtg = -6           :'falls zu weit runter, bei -6 festnageln
   IF ZRichtg >  5 THEN ZRichtg =  5           :'falls zu weit zurück, bei 10 festnageln
   IF ZRichtg < -10 THEN ZRichtg = -10           :'falls zu weit vor,    bei -6 festnageln
   glClear GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT  :'bisherig erstellte Objekte löschen, unsere 3D-Welt wieder von Neuem an erstellen
   glPushMatrix                                        :'aktuelle Position + Drehgrade sichern (2.Zettel mit gleicher Pos auf PositionsSTACK)
    glRotatef WeltDrehY, 0, 1, 0                       :'Rotationsbefehl der Kamera, um Y ist zu drehen
    glTranslatef XRichtg, YRichtg, ZRichtg             :'Verschiebung der Kamera, auf neue Werte einstellen

    '-------------------------
    'AB HIER OBJEKTE ERSTELLEN
    '-------------------------
    glPointSize(5)                             :' Punktgröße auf 5 = deutlich groß
    glColor3f 1.0,0.0,0.0                      :' Anzeigefarbe auf ROT setzen
    Koordinatensystem("Anzeigen", "", 0, 0, 0) :' den Stringwert "Anzeigen" übergeben
    Schachbrettboden("BeschreibungsListeBoden", "", 0, 0, 0):' SchachbrettBoden in der 3D-Welt erstellen
    glPushMatrix                                                             :'akt.Position und Drehgrade sichern
    Pyramide("BeschreibungsListe"  , "", PyraXDrehw, PyraYDrehw, PyraZDrehw) :'die erste Pyramide
    glPopMatrix                                                              :'auf gesichterte Position und Drehgrade zurücksetzen
    glPushMatrix                                                             :'akt.Position und Drehgrade sichern
    glTranslatef -2, 0, 0                                                    :'hier der Verschiebebefehl, damit die 2.Pyramide woanderst steht
    Pyramide("BeschreibungsListe"  , "", PyraXDrehw, PyraYDrehw, PyraZDrehw) :'und eine zweite Pyramide
    glPopMatrix                                                              :'auf gesichterte Position und Drehgrade zurücksetzen

    '-----------------
    'OBJEKTE ERSTELLEN
    '-----------------

'-------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXX RECHTECK MIT TEXTUR ERZEUGEN XXXXXXXXXXXXXXXX
'-------------------------------------------------------------------
   glEnable GL_TEXTURE_2D          :'Texturieren mit dem grad aktiven Bild einschalten/erlauben
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL) :'texturieren "voll drauf" kein durchsichtig etc
   glBindTexture GL_TEXTURE_2D, TexturNummer      :'Unser Bild auf der Grafikkarte als aktives setzen
   glColor3f 1.0,0.0,0.0                          :'Anzeigefarbe (für das Rechteck) auf ROT setzen
   glBegin GL_QUADS                               :'Beginn Beschreibungsliste Objekt Viereck
      glTexCoord2d 0.0, 1.0 : glVertex3f -1.0,  1.0, -4.0  :'die linke obere Ecke des Rechtecks
      glTexCoord2d 0.0, 0.0 : glVertex3f -1.0, -1.0, -4.0  :'die linke untere Ecke des Rechtecks
      glTexCoord2d 1.0, 0.0 : glVertex3f +1.0, -1.0, -4.0  :'die rechte untere Ecke des Rechtecks
      glTexCoord2d 1.0, 1.0 : glVertex3f +1.0,  1.0, -4.0  :'die rechte obere Ecke des Rechtecks
   glEnd                                          :'Fertig mit dem Objekt
   glDisable GL_TEXTURE_2D         :'Texturieren ausschalten (es soll ja nur das Viereck texturiert werden, nicht die Pyramiden)
'-------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
'-------------------------------------------------------------------


    flip                                               :'liebes OpenGL, zeig alles, was in der Schleife für dich vornedran steht, auf Monitor an
   '---------------------------
   'Ende der Schleife
   '---------------------------
   glPopMatrix                                         :' Aufgabe erledigt, den zweiten Zettel mit der geänderten Pos wegschmeißen, dann ist Alte Pos wieder aktuelle Pos
LOOP

END

'--------------------------------
SUB Koordinatensystem(was AS STRING, TxtPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
   DIM AS INTEGER Zaehler
   STATIC AS INTEGER AnzeigeSchalter
   SELECT CASE UCASE(was)
      CASE "ANAUS"
         IF AnzeigeSchalter = 0 THEN AnzeigeSchalter = 1 ELSE AnzeigeSchalter = 0
      CASE "ANZEIGEN"
        IF AnzeigeSchalter = 1 THEN
         glBegin GL_LINES:' Ja, wir beginnen mit der Beschreibungsliste fuer Linien
         'und zwar zuerst die positiven Bereiche jeder Achse
         glColor3f 1.0, 1.0, 1.0        :' Positives Maß = weiße Teilstriche (Rot 1.0, blau 1.0, grün 1.0 = weiß)
         FOR Zaehler = 1 TO 6 :'fuer die Maßeinheitspositionen 1, 2 und 3 Striche Ziehen
            'X-ACHSE
               'senkrechter Strich (wie Y-Achse)
            glVertex3f  Zaehler+Para1,  0.2+Para2,  0.0+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1, -0.2+Para2,  0.0+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
               'waagerechter Strich (wie Z-Achse)
            glVertex3f  Zaehler+Para1,  0.0+Para2,  0.2+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1,  0.0+Para2, -0.2+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
            'Y-ACHSE
               'Strich wie X-Achse
            glVertex3f  0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach +X, Tiefe = 0
            glVertex3f -0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach -X, Tiefe = 0
               'Strich wie Z-Achse
            glVertex3f  0.0+Para1, Zaehler+Para2,  0.2+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach vorne/+Z, Seite (X) = 0
            glVertex3f  0.0+Para1, Zaehler+Para2, -0.2+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach hinten/-Z, Seite (X) = 0
            'Z-ACHSE
               'Strich waagerecht (X)
            glVertex3f  0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach rechts +X, Hoehe = 0
            glVertex3f -0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach links -X, Hoehe = 0
               'Strich senkreicht (Y)
            glVertex3f  0.0+Para1,  0.2+Para2,  Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach oben +Y, Seite = 0
            glVertex3f  0.0+Para1, -0.2+Para2,  Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach unten -Y, Seite = 0
         NEXT Zaehler
         glColor3f 0.0, 0.0, 0.0        :' Negatives Maß = schwrze Teilstriche (alles auf 0)
         FOR Zaehler = -6 TO -1 :'fuer die Maßeinheitspositionen -3, -2 und -1 Striche Ziehen
         'X-ACHSE
            'senkrechter Strich (wie Y-Achse)
            glVertex3f  Zaehler+Para1,  0.2+Para2,  0.0+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1, -0.2+Para2,  0.0+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
            'waagerechter Strich (wie Z-Achse)
            glVertex3f  Zaehler+Para1,  0.0+Para2,  0.2+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1,  0.0+Para2, -0.2+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
            'Y-ACHSE
               'Strich wie X-Achse
            glVertex3f  0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach +X, Tiefe = 0
            glVertex3f -0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach -X, Tiefe = 0
               'Strich wie Z-Achse
            glVertex3f  0.0+Para1, Zaehler+Para2,  0.2+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach vorne/+Z, Seite (X) = 0
            glVertex3f  0.0+Para1, Zaehler+Para2, -0.2+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach hinten/-Z, Seite (X) = 0
            'Z-ACHSE
               'Strich waagerecht (X)
            glVertex3f  0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach rechts +X, Hoehe = 0
            glVertex3f -0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach links -X, Hoehe = 0
               'Strich senkreicht (Y)
            glVertex3f  0.0+Para1,  0.2+Para2,  Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach oben +Y, Seite = 0
            glVertex3f  0.0+Para1, -0.2+Para2,  Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach unten -Y, Seite = 0
         NEXT Zaehler
         'UND NUN DIE DREI ACHSEN SELBST:
         'X-ACHSE
         glColor3f 1.0, 0.0, 0.0        :' Xachse = rot
         glVertex3f  -6.0+Para1, 0+Para2, 0+Para3
         glVertex3f  +6.0+Para1, 0+Para2, 0+Para3
         'Y-ACHSE
         glColor3f 0.0, 1.0, 0.0        :' Yachse = grün
         glVertex3f  0+Para1, -6.0+Para2, 0+Para3
         glVertex3f  0+Para1, +6.0+Para2, 0+Para3
         'Z-ACHSE
         glColor3f 0.0, 0.0, 1.0        :' Zachse = blau
         glVertex3f  0+Para1, 0+Para2, -6.0+Para3
         glVertex3f  0+Para1, 0+Para2, +6.0+Para3
         glEnd
       END IF
   END SELECT
END SUB
'-------------------------
SUB Schachbrettboden(was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
   STATIC AS SINGLE QuadKantenLaenge, QuadsQuer, QuadsTief
   STATIC AS SINGLE Farbe1Rot, Farbe1Gruen, Farbe1Blau
   STATIC AS SINGLE Farbe2Rot, Farbe2Gruen, Farbe2Blau
   STATIC AS SINGLE StartPosX, StartPosZ, BodenHoehe
   STATIC AS INTEGER ZaehlerX, ZaehlerZ                   :'ForNext-Zählvars, ggf. rekursiver aufruf dieser Sub, drum STATIC
   SELECT CASE UCASE(was)
      CASE "SETZKANTENLAENGE"
         'Aufruf hierfür in Para1 die Länge der Kanten der Quadrate
         QuadKantenLaenge = Para1
      CASE "SETZQUADSZAHL"
         'Aufruf hierfür in Para1 die Anzahl von Quads quer,
         'in Para2 die Anzahl in der Tiefenrichtung
         QuadsQuer = Para1
         QuadsTief = Para2
      CASE "SETZFARBE1"
         'in Para1 Para2 Para2 die Rot Grün und Blauwerte der ersten Quadratfarbe
         Farbe1Rot   = Para1
         Farbe1Gruen = Para2
         Farbe1Blau  = Para3
      CASE "SETZFARBE2"
         'in Para1 Para2 Para2 die Rot Grün und Blauwerte der ersten Quadratfarbe
         Farbe2Rot   = Para1
         Farbe2Gruen = Para2
         Farbe2Blau  = Para3
      CASE "SETZSTARTPOS"
         'in Para1 Para2 Para2 die X und Z Position, von wo begonnen wird,
         'die vielen Quadrate zu erstellen
         StartPosX   = Para1
         StartPosZ   = Para2
         BodenHoehe  = Para3
      CASE "BESCHREIBUNGSLISTEBODEN"
         'Hier erstellen wir die FOR-NEXT-SCHLEIFEN,
         'welche die OpenGL-Beschreibungsliste
         'zum Anzeigen des Bodens erstellen
         glBegin GL_QUADS
            FOR ZaehlerX = 1 TO QuadsQuer     :'Laufnr des grad zu zeichnenden Quads auf der X-Achse
               FOR ZaehlerZ = 1 TO QuadsTief  :'Laufnr des grad zu zeichnenden Quads auf der Z-Achse
                  'Die Farbe festlegen
                  IF ((ZaehlerX+ZaehlerZ)\2)*2=(ZaehlerX+ZaehlerZ) THEN
                     'Wenn die Summe von ZahlerX+ZaehlerY gerade ist Farbe1 nehmen
                     glColor3f Farbe1Rot, Farbe1Gruen, Farbe1Blau
                  ELSE
                     'Wenn die Summe von ZahlerX+ZaehlerY UNgerade ist Farbe2 nehmen
                     glColor3f Farbe2Rot, Farbe2Gruen, Farbe2Blau
                  END IF
                  'Die Eckpunkte der Quadrate benennen wir in der Reihenfolge GEGEN DEN UHRZEIGERSINN, von oben draufschauend
                  glVertex3f StartPosX+(ZaehlerX-1)*QuadKantenLaenge,  BodenHoehe,  StartPosZ+(ZaehlerZ-1)*QuadKantenLaenge
                  glVertex3f StartPosX+ ZaehlerX   *QuadKantenLaenge,  BodenHoehe,  StartPosZ+(ZaehlerZ-1)*QuadKantenLaenge
                  glVertex3f StartPosX+ ZaehlerX   *QuadKantenLaenge,  BodenHoehe,  StartPosZ+ ZaehlerZ   *QuadKantenLaenge
                  glVertex3f StartPosX+(ZaehlerX-1)*QuadKantenLaenge,  BodenHoehe,  StartPosZ+ ZaehlerZ   *QuadKantenLaenge
               NEXT ZaehlerZ
            NEXT ZaehlerX
         glEnd
      CASE ELSE
         'Hier kommen alle SUB-Aufrufe an, welche als
         'was-Parameter einen Eintrag haben, der hier
         'nicht ausgewertet wurde.
         'Tippfehler vom Programmierer????
   END SELECT
END SUB
'-------------------------
SUB Pyramide(was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
   'Pyramide erstellen, Grundfläche/Quadrat = auf Höhe 0, seitlich mittig auf X- und Z-Achse Positioniert
   'Grundflächengroesse = XLaenge x ZLaenge, Höhe Pyramide = ZLaenge
   STATIC AS SINGLE XLaenge, YLaenge, ZLaenge
   STATIC AS SINGLE Farbe1Rot, Farbe1Gruen, Farbe1Blau
   STATIC AS SINGLE Farbe2Rot, Farbe2Gruen, Farbe2Blau
   STATIC AS SINGLE Farbe3Rot, Farbe3Gruen, Farbe3Blau
   STATIC AS SINGLE Farbe4Rot, Farbe4Gruen, Farbe4Blau
   STATIC AS SINGLE Farbe5Rot, Farbe5Gruen, Farbe5Blau
   STATIC AS INTEGER ZaehlerX, ZaehlerZ                   :'ForNext-Zählvars, ggf. rekursiver aufruf dieser Sub, drum STATIC
   SELECT CASE UCASE(was)
      CASE "SETZLAENGEN"  : XLaenge   = Para1 : YLaenge     = Para2 : ZLaenge     = Para3
      CASE "SETZFARBE1"   : Farbe1Rot = Para1 : Farbe1Gruen = Para2 : Farbe1Blau  = Para3 :'ein Dreieck
      CASE "SETZFARBE2"   : Farbe2Rot = Para1 : Farbe2Gruen = Para2 : Farbe2Blau  = Para3 :'ein Dreieck
      CASE "SETZFARBE3"   : Farbe3Rot = Para1 : Farbe3Gruen = Para2 : Farbe3Blau  = Para3 :'ein Dreieck
      CASE "SETZFARBE4"   : Farbe4Rot = Para1 : Farbe4Gruen = Para2 : Farbe4Blau  = Para3 :'ein Dreieck
      CASE "SETZFARBE5"   : Farbe5Rot = Para1 : Farbe5Gruen = Para2 : Farbe5Blau  = Para3 :'der Boden/Quadrat
      CASE "BESCHREIBUNGSLISTE"
         glrotatef Para1, 1, 0, 0    :'<----------- um X-Achse drehen
         glrotatef Para2, 0, 1, 0    :'<----------- um Y-Achse drehen
         glrotatef Para3, 0, 0, 1    :'<----------- um Z-Achse drehen
         'die OpenGL-Beschreibungsliste zum Anzeigen der Pyramide erstellen
         glBegin GL_QUADS
            'der Boden der Pyramide als Quadrat, auf Höhe 0(Y-Achse),
            'seitliche Ausrichtungen = Quadratmitte = X-Achsenmitte und Z-Achsenmitte
            'damit Zentriert sitzt, für 1.Punkt einfach halbe Kantenlänge von 0 Abziehen, für 2. dazuaddieren
            'Reihenfolge Eckpunktangabe gegen Uhrzeigersinn VON UNTEN her gesehen (unten=Außenseite später)
            glColor3f  Farbe5Rot        , Farbe5Gruen      , Farbe5Blau
            glVertex3f 0-(XLaenge/2)    , 0  , 0+(ZLaenge/2)
            glVertex3f 0-(XLaenge/2)    , 0  , 0-(ZLaenge/2)
            glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2)
            glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2)
         glEnd
         glBegin GL_TRIANGLES
            '   Dreieckseite 1 =an Kante 1.+2.Quadratpunkt
            glColor3f  Farbe1Rot        , Farbe1Gruen      , Farbe1Blau
            glVertex3f 0-(XLaenge/2)    , 0       , 0-(ZLaenge/2)
            glVertex3f 0-(XLaenge/2)    , 0       , 0+(ZLaenge/2)
            glVertex3f 0                , YLaenge , 0
            '   Dreieckseite 2 =an Kante 2.+3.Quadratpunkt
            glColor3f  Farbe2Rot        , Farbe2Gruen      , Farbe2Blau
            glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2)
            glVertex3f 0-(XLaenge/2)    , 0  , 0-(ZLaenge/2)
            glVertex3f 0                , YLaenge , 0
            '   Dreieckseite 3 =an Kante 3.+4.Quadratpunkt
            glColor3f  Farbe3Rot        , Farbe3Gruen      , Farbe3Blau
            glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2)
            glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2)
            glVertex3f 0                , YLaenge , 0
            '   Dreieckseite 4 =an Kante 3.+1.Quadratpunkt
            glColor3f  Farbe4Rot        , Farbe4Gruen      , Farbe4Blau
            glVertex3f 0-(XLaenge/2)    , 0  , 0+(ZLaenge/2)
            glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2)
            glVertex3f 0                , YLaenge , 0
         glEnd
      CASE ELSE
         'Hier kommen alle SUB-Aufrufe an, welche als
         'was-Parameter einen Eintrag haben, der hier
         'nicht ausgewertet wurde.
         'Tippfehler vom Programmierer????
   END SELECT
END SUB

Und so sieht das ganze aus, wenn man ein bißchen in unserer 3D-Welt
per Cursor- bzw. Strg+Cursor-Tasten herumspaziert ist:
Ogl_Tut_5_texturiertes_Quad
mit X/x/Y/y/Z/z-Tasten die Pyramiden drehen geht auch :-)

Schön, aber fehleranfällig. Wir prüfen ja nicht, ob die geplante
Datei Mauer_128.bmp wirklich im Verzeichnis steht. Genauso wenig
checken wir ab, ob das Bild darin auch wirklich 128x128 Pixel
groß ist, ob es überhaupt eine Bitmap-Datei ist...
[seitenwechsel]

TEXTUREN IN EINEM UNTERPROGRAMM LADEN - VERWENDBAR

Na, hat Ihnen das Schnupperbeispiel gefallen?

Aber es hat ja den Haken, daß es nur mit der Datei "Mauer_128.bmp"
funktioniert, ansonsten nicht.
Das Beispiel war ja auch nur dazu gedacht, Ihnen die notwendigen
FreeBasic- und OpenGL-Funktionen mal näherzubringen, mit denen
man Texturen auf ein Objekt bringt.

Nun wollen wir das Erarbeitete in ein Unterprogramm packen, welchem
wir einfach den Dateinamen einer Bitmapdatei übergeben und den
Namen/Nummer der OpenGL-Textur zurückerhalten, mit dem wir diese
dann aktivieren können.

Aber ergänzenderweise muß ich sagen, daß ich solangsam Bedenken
habe, weil wir bisher immer ohne Fehlerauswertung gearbeitet haben.
Ohne diese wird zwar alles etwas übersichtlicher, aber nun arbeiten
wir mit Dateien, und da geht ganz schnell mal was schief.

Also wird diese SUB die Bitmap-Datei analysieren und prüfen,
ob sie überhaupt verwendbar ist. Falls nicht, müssen entsprechende
Fehlermeldungen raus und das Programm beendet werden.

eine Fehlerausgabe

Deshalb erstellen wir erst mal eine Sub für diese Fehlermeldungen,
in welcher die Meldungen auf dem Console-Fenster und auch in einer
Datei ausgegeben werden.

Das Ausgeben von Texten in dem Consolefenster kennen Sie ja bereits,
das zusätzliche Schreiben in eine Datei sollte Ihnen auch nicht
fremd sein. Also hier diese SUB:

SUB Schreibe(Text AS STRING)
   DIM AS INTEGER DateiNummer
   'AUF DEN MONITOR IN DIE TEXTCONSOLE
   DateiNummer=FREEFILE
   OPEN "CONS" FOR OUTPUT AS #DateiNummer
   PRINT #DateiNummer, Text
   CLOSE #DateiNummer
   'NOCHMAL IN EINE DATEI
   DateiNummer=FREEFILE
   OPEN "Protokoll.txt" FOR APPEND AS #DateiNummer
   PRINT #DateiNummer, DATE & " " & TIME & " " & Text
   CLOSE #DateiNummer
END SUB

Die Datei benennen wir "Protokoll.txt". Damit diese nicht bei
jedem Aufruf von Schreibe() gelöscht werden würde, öffnen wir
die Datei für "Append" = anhängen.
Damit wird diese Datei aber nie gelöscht, sodaß bei einem
Zweiten/Dritten/... Start unseres Programmes die Protokoll-
einträge immer noch unten dran geschrieben werden.
Um jetzt die Einträge zuordnen zu können, schreiben wir bei
jeder Ausgabe das Datum und die Uhrzeit vornedran.

Im DeclareBereich natürlich diese SUB auch declarieren

DECLARE SUB Schreibe(Text AS STRING)

So, nun können wir uns an das Unterprogramm für das Bildeinlesen
heranwagen. Nochmal, zur Veranschaulichung. Bei dieser Routine
soll es darum gehen, ein Bild auf der Festplatte an OpenGL zu
übergeben, sodaß man später mit diesem Bild Texturieren kann.
Dieser Vorgang sollte gleich nach dem Initialisieren von OpenGL
aufgerufen werden, sodaß das Bild im Speicher der Grafikkarte
steht.
Erst viel später werden dann Objekte erstellt, und ggf. diese
mit dem Bild texturiert, somit kommt das Texturieren selber
hier nicht rein.

Hier also "nur" das Kopieren des Bildes auf die Grafikkarte.
Im Grunde nehmen wir die selben Befehle wie zuvor, nur daß
wir nun die Datei prüfen, aus der Datei die Bildgröße holen,
sodaß wir nur den Namen der Datei an die Sub übergeben müssen.

von der Festplatte in den Arbeitsspeicher

Tja, hierbei kommt der größe neue Brocken auf uns zu.
Das Prüfen der übergebenen Datei, ob diese für Texturen
verwendbar ist.
1.Check: "ist die Datei überhaupt da?"
2.Check: "ist das überhaupt eine Bitmap-Datei?"
3.Check: "ist das Bild da drin auch wirklich UNKomprimiert?"
4.Check: "Ist die Größe des Bildes für Texturen verwendbar?"
-------- Da wir immer noch für sämtliche OpenGL-Versionen
-------- ab Version 1.0 schreiben, hab ich die Größen auf
-------- den für alle passenden Werte festgelegt:
-------- A) Bild muß quadratisch sein
-------- B) Die Kantenlängen dürfen
-------- 64Pixel, 128Pixel oder 256Pixel lang sein

Nur wenn alle Bedingungen erfüllt sind, kann jedes OpenGL
das Bild als Textur verwenden.

Oh Gott, was hat er denn nun vor? werden Sie denken, aber
es ist leichter, als es klingt.
In jeder Bitmapdatei stehen vor den eigentlichen Bilddaten
einige Infos drin, anhand derer wir das Ganze überprüfen
werden. Diese Infos sehen wie folgt aus:

Bitmapdatei Header-Aufschlüsselung

Position Inhalt VarTyp zum Einlesen:
01 bis 02Dateiformat immer "BM" für Bitmap 2 x UByte Als Chr()Wert
03 bis 06DateigroesseBytes (unzuverlässig) Integer
07 bis 10Null Integer
11 bis 14Position der Bilddaten (Pointerwert) Integer
15 bis 18Länge des Headers ab hier Integer
19 bis 22Bildbreite (Pixel) Integer
23 bis 26Bildhöhe (Pixel) Integer
27 bis 28Ebenenanzahl (sollte 1 sein) Short
29 bis 30Farbtiefe (Bits pro Pixel) Short
31 bis 34Kompressionsmethode (0=keine) Integer
35 bis 38Datenmenge ohne Header Integer
39 bis 42horizontale Auflösung (Pixel/Meter) Integer
43 bis 46vertikale Auflösung (Pixel/Meter) Integer
47 bis 50Anzahl benutzter Farben (bei 1/2/4 oder 8-bit) Integer
51 bis 54Anzahl wichtiger Farben (bei 1/2/4 oder 8-bit) Integer

Die Positionen gelten für das Einlesen per GET#, das Erste Byte ist dort Nr 1 (nicht 0)
ANMERKUNG ZUR Bildhoehe: Wenn der Wert Negativ ist, ist das Bild in der Datei vertikal gespiegelt!


hier finden wir also alles, was wir brauchen.

Also basteln wir das erste Stück, das Überprüfen der Datei:

Nun hoffe ich, daß Sie mit dem Umgang von FreeBasic vertraut
sind, daß Sie IF-THEN und deren Verschachtelung verstehen.

Denn wir packen die oben genannte 4 Checks in vierfach
verschachtelte IF-THEN-Prüfungen, wobei wir immer auf Falsch
prüfen, ggf. das Programm mit Fehlermeldung beenden, Falls nicht
falsch(ELSE), machen wir da drin weiter.
Bedeutet schlichtweg, wenn der Programmablauf durch alle
vier IF-THENs durchläuft, und das Programm sich nicht
beendet, weil eine Bedingung nicht paßt, dann hat die
übergebenen Datei volle Gültigkeit, deshalb wird in der
vierten IF-Bedingung bei ELSE gleich der RAM per ImageCreate
reserviert und die Datei per BLOAD dort rein kopiert.

Die Routine also als Function dimmen, damit wir Werte an
den aufrufenden Befehl zurückgeben können.
Die Funktion nennen wir mal BmpToOgl.
Als Erstes brauchen wir dann ein paar Variablen:

FUNCTION BmpToOgl(BmpName AS STRING) AS INTEGER
   DIM AS BYTE PTR DateiImRamPtr, SpBmpPixelZeiger
   DIM AS INTEGER DateiNummer, Ganzzahl, Breite, Hoehe
   DIM AS UINTEGER TexturNummer, RotWert, BlauWert, ZeilenZaehler, PunktZaehler
   DIM AS UBYTE Buchstabe
   DIM AS STRING Dummy

Danach können wir gleich mit dem Datei-Check beginnen:

   DateiNummer = FREEFILE
   OPEN BmpName FOR INPUT AS #DateiNummer
   IF LOF(DateiNummer) = 0 THEN
      'BildDatei Existiert nicht, die Datei hat eine Länge von NULL
      Schreibe("Die Bilddatei " & BmpName & " ist nicht vorhanden oder hat Laenge 0! breche ab")
      CLOSE #DateiNummer
      END
   ELSE
      'BildDatei ist vorhanden, String aus den ersten zwei Bytes in der Datei basteln
      GET #DateiNummer, 1, Buchstabe
      Dummy=CHR(Buchstabe)
      GET #DateiNummer, 2, Buchstabe
      Dummy = Dummy + CHR(Buchstabe)
      IF Dummy <> "BM" THEN
         'keine Bitmap-Datei
         Schreibe("Die Bilddatei " & BmpName & " ist keine Bitmapdatei! Kennung=" & Dummy & " breche ab")
         CLOSE #DateiNummer
         END
      ELSE
         'Datei ist Bitmap, prüfen ob komprimiert
         GET #DateiNummer, 31, Ganzzahl
         IF Ganzzahl <> 0 THEN
            'Bitampdatei ist komprimiert
            Schreibe("Die Bilddatei " & BmpName & " ist komprimiert gespeichert, kann damit nicht umgehen, breche ab!")
            CLOSE #DateiNummer
            END
         ELSE
            'unkomprimierte Bitmap-Datei, prüfen ob quadratisch und 64/128/256 bytes kantenlänge
            GET #DateiNummer, 19, Breite
            GET #DateiNummer, 23, Hoehe
            IF (Breite <> Hoehe) OR (Breite <> 64 AND Breite <> 128 AND Breite <> 256) THEN
               'Bild ist entweder nicht quadratisch oder nicht 64/128/256er Kantenlängen
               Schreibe("Die Bilddatei " & BmpName & " ist entweder nicht Quadratisch oder hat keine Kantenlänge von 64, 128 oder 256 Pixel")
               Schreibe("       Breite:" & Breite & " Hoehe:" & Hoehe & "   breche ab!")
               CLOSE #DateiNummer
               END
            ELSE
               '***************************************
               'Bilddatei ist OK, kann verwendet werden
               '***************************************
               CLOSE #DateiNummer
               DateiImRamPtr = IMAGECREATE(Breite, Hoehe, 0, 32)
               BLOAD(BmpName, DateiImRamPtr)
            END IF
         END IF
      END IF
   END IF

So, wenn das alles durch ist, das Programm nicht abgebrochen hat,
dann haben wir nun in DateiImRamPtr den Zeiger auf das Bild
im Arbeitsspeicher, das Bild ist bereits dahin kopiert.

Farben Rot und Blau tauschen

Genauso, wie im ersten Beispiel, setzen wir nun gleich die
vertauschten Rot- und Blau-Werte richtig:

   SpBmpPixelZeiger = DateiImRamPtr + Len(FB.IMAGE)
   FOR ZeilenZaehler = 0 TO Hoehe-1
      FOR PunktZaehler = 0 TO Breite-1
         BlauWert = SpBmpPixelZeiger[0]        :'aus Ram das Byte mit dem Blauwert des grad zu bearbeitenden Pixels holen
         RotWert  = SpBmpPixelZeiger[2]        :'aus Ram das Byte mit dem Rotwert des grad zu bearbeitenden Pixels holen
         SpBmpPixelZeiger[0] = RotWert         :'den Rotwert an die Stelle des Blauwertes im RAM schreiben
         SpBmpPixelZeiger[2] = BlauWert        :'den Blauwert an die Stelle des Rotwertes im RAM schreiben
         SpBmpPixelZeiger = SpBmpPixelZeiger+4 :'Ram-Adresse um 4 bytes erhöhen = Start Pixeldaten des nächsten Pixels
      NEXT PunktZaehler
   NEXT ZeilenZaehler

Auch hier wieder, wir nehmen (noch) keine Rücksicht darauf,
das unser Bild nun senkrecht gespiegelt ist, damit uns ein
einziges ImageCreate ausreicht


Nun kommt nochmal etwas Neues dazu. Am Anfang hatten wir doch
festgestellt, daß "neuere" Grafikkarten viel schneller arbeiten,
wenn die Bilddaten mit 32-bit im Speicher stehen.
Aber feststellen, ob die Karte nun schnell oder alt ist, läßt
sich nicht zuverlässig.
Nun hab ich dazu eine andere Überlegung. Wenn ich ein OpenGL
- Programm erstelle, liste ich dem Anwender gerne die Möglich-
keiten seiner Grafikkarte auf und er soll auswählen, mit
welcher Auflösung er das Programm laufen lassen will.
Und genau da häng ich mich dann: Wenn er eine ältere Karte
hat, wird er wohl kaum auf 32-bit Farbtiefe schalten, hat er
eine neuere, klickt er freudig auf "bitte 32-bit einstellen".
Also prüfen wir hier einfach, mit welcher Farbtiefe das
OpenGL-Fenster grad läuft.
Bei 32-bittigem OpenGL-Fenster legen wir das Texturbild
ebenfalls mit 32-bit Farbtiefe auf der Grafikkarte ab (GL_RGBA)
ansonsten halt mit 24- oder 16-bit (GL_RGB, ob 16 oder 24
zu nehmen ist, regelt OpenGL automatisch).
Jedenfalls müssen wir eine Variable anlegen, in der wir
die Farbtiefe des OpenGL-Fensters speichern und mit dem
FreeBasic-Befehl SCREENCONTROL die Farbtiefe in der Variablen
ablegen:

Auf Grafikkarte kopieren lassen

   DIM AS INTEGER FensterFarbTiefe
   SCREENCONTROL 5, FensterFarbTiefe :' 5 = GET_SCREEN_DEPTH : Farbtiefe in Bits des OpenGL-Fensters ermitteln

Nun wieder wie gehabt, für ein neues Texturbild einen Eintrag
erstellen lassen, sich den Namen(Nummer) der Textur geben
lassen:

   glGenTextures 1, @TexturNummer
   glBindTexture GL_TEXTURE_2D, TexturNummer

und je nachdem, was in FensterFarbTiefe drin steht, das
Bild im Ram auf die Grafikkarte abholen lassen:

   IF FensterFarbTiefe = 32 THEN
      glTexImage2D GL_TEXTURE_2D, 0, GL_RGBA, Breite, Hoehe, 0, GL_RGBA, GL_UNSIGNED_BYTE, DateiImRamPtr+Len(FB.IMAGE)
   ELSE
      glTexImage2D GL_TEXTURE_2D, 0, GL_RGB, Breite, Hoehe, 0, GL_RGBA, GL_UNSIGNED_BYTE, DateiImRamPtr+Len(FB.IMAGE)
   END IF

Die Parameter für das Texturieren bei nah dran und weit weg
einstellen:

   glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR
   glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR

Nachdem nun das Bild auf der Grafikkarte steht, können
wir das Bild im Arbeitsspeicher wieder löschen und den
Speicher wieder freigeben:

   ImageDestroy(DateiImRamPtr)

Da wir unsere Einleseroutine als Function angelegt haben,
sorgen wir nun noch dafür, daß der Name(Nummer) der
Textur an den aufrufenden Befehl zurückgegeben wird:

   BmpToOgl=TexturNummer
END FUNCTION

Eine Zweite Textur

Ja, Sie lesen richtig, da wir die Funktionen für das Bild
von Festplatte bis zur Grafikkarte kopieren in eine
Function ausgelagert haben, können wir diese ja auch
mehrfach aufrufen. So wie die Function angelegt ist,
stört sie sich nicht daran, mehrmals laufen zu müssen.
Das Einzigste, was wir brauchen, ist eine zweite INTEGER
Variable, damit wir den Namen(Nummer) der zweiten
Textur auch speichern können:

DIM AS UINTEGER TexturNummer, TexturZwei :'Texturnummer war ja schon, TexturZwei ist für die zweite Textur

Jetzt sollten wir aber auch ein anderes Bild haben,
zweimal das Gleiche wär ja langweilig.
Ist zwar irgendwie verrückt, aber ich hab eins mit
Kirschen, das gefällt mir irgendwie und ist lustig:
Kirschen_256.bmp
Die Datei hat den Namen Kirschen_256. Sie merken bestimmt,
dieses Bild ist größer, hat eine Seitenlänge von 256 Pixel.

Klicken Sie das Bild mit der rechten Maustaste an und
wählen Sie "Speichern unter".
Speichern Sie die Datei dorthin, wo bereits Mauer_128.bmp steht,
also in das Verzeichnis, wo auch das kompilierte, ausführbare
Programm stehen wird.

Nachdem das Bild nun an der richtigen Stelle auf der Festplatte
gespeichert ist, können wir es als Textur an OpenGL übergeben.
Mit unserer Function ist das ein Leichtes, einfach Aufrufen,
als Parameter nun "Kirschen_256.bmp" übergeben, und die Rückgabe
der Function (Texturname/nummer) in der Variablen TexturZwei
speichern:

TexturZwei   =  BmpToOgl("Kirschen_256.bmp")   ' eine zweite Bitmapdatei als Textur an OpenGL schicken

Schwupps, das wars schon. Nun können wir bereits die Kirschen
als Textur einsetzen - aber wo?

Klar doch, jetzt gehts an die Pyramiden ;-)
Dabei müssen wir nur beachten, daß wir die beiden Pyramiden
mit einer einzigen Routine erstellen, bei der wir direkt
davor per glTranslatef die Position, an der zu erstellen ist,
ändern.
Das bedeutet, wenn wir direkt in der Pyramidensub das verwenden
der Textur reinschreiben, haben beide Pyramiden die gleiche
Textur.
Soll heißen: In der SUB schreiben wir nur die TexturKoordinaten
vor die 3D-Punkte, das aktivieren eines Bildes als Textur machen
wir schon im Hauptprogramm, somit können wir erst die Mauertextur
aktivieren, eine Pyramide erstellen, dann die Kirschentextur
aktivieren und die zweite Pyramide erstellen.

den Pyramiden Texturkoordinaten geben

Also gehen wir in die PyramidenSUB. Dort stehen im Kern dann
die Beschreibungslisten des Quadrates als Boden und der
vier Dreiecke als Seitenflächen.
Fangen wir mit dem Boden an, ein Texturbild auf ein Quadrat,
das hatten wir ja schon.
Einfach direkt vor jede Punktdeffinition(glVertex3f) die
Punktangabe auf dem TexturBild setzen. Damit der Zusammenhang
deutlich zu sehen ist, schreiben wir die Texturpunktdeffinition
und den 3D-Punkt(Vertex) in eine Zeile, wie wir wissen, müssen
wir mehrere FreeBasic-Befehle in einer Zeile mit Doppelpunkt
voneinander trennen.
Für das bestimmen, ob die Ecke des Quads nun links/oben oder
rechts/oben usw ist, müssen Sie sich grundsätzlich daran
halten, von "außen" auf das Objekt schauen.
Das ist bei unserem Pyramidenboden in der 3D-Welt von unten
nach oben schauen. Und in der Sichtweise arbeiten wir dann
GEGEN den Uhrzeigersinn.
Hier der Auszug der Deffinition Pyramidenboden=QUAD:

         glBegin GL_QUADS
            glColor3f  Farbe5Rot, Farbe5Gruen, Farbe5Blau :'falls Texturieren Disabled ist, wird diese Farbe genommen
            glTexCoord2d 0.0, 1.0 : glVertex3f 0-(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'Ecke links oben
            glTexCoord2d 0.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'Ecke links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'Ecke rechts unten
            glTexCoord2d 1.0, 1.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'Ecke rechts oben
         glEnd

Und nun die Dreiecke der Seitenflächen, man beachte hier,
daß wir nun vier weitere Objekte haben, und auf diese
genauso wie auf dem Quad des Bodens das selbe Texturbild
kleben. Also ein Bild für viele Objekte, kein Problem.

Jedoch der Übersichtlichkeit hier nochmal eine Skizze,
wie wir auf dem Texturbild die TexturKoordinaten bei
Dreiecken setzen:
Ogl_tut_5_Texturkoordinaten_fuer_Dreieck
Sie sehen, wir schneiden aus dem Bild per Texturkoordinaten
einfach eine passende Fläche aus. Genau wegen solchen
Fällen gibt es diese Texturkoordinaten, egal ob Sterne,
Dreiecke, oder sonstige unförmige Objekte, jeden Punkt
des Objekt als Punkt auf dem Texturbild so passend
deffinieren, daß eine passende Form ausgeschnitten wird,
schon paßts.

Also gehen wirs an, die vier Dreiecke sind in unserer
Sub jedes für sich deffiniert, also bei allen vier
die Texturkoordinaten davor setzen. Auch hier wieder
von außen auf das Objekt schauend, gegen den Uhrzeigersinn
die Punktreihenfolgen wählen.
Auszug der Dreiecke:

         glBegin GL_TRIANGLES
            '   Dreieckseite 1 = linke Seite
            glColor3f  Farbe1Rot, Farbe1Gruen, Farbe1Blau
            glTexCoord2d 0.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0       , 0-(ZLaenge/2) :'links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0       , 0+(ZLaenge/2) :'rechts unten
            glTexCoord2d 0.1, 1.0 : glVertex3f 0                , YLaenge , 0             :'Spitze oben
            '   Dreieckseite 2 = hintere Seite /Rückseite
            glColor3f  Farbe2Rot, Farbe2Gruen, Farbe2Blau
            glTexCoord2d 0.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'rechts unten
            glTexCoord2d 0.5, 1.0 : glVertex3f 0                , YLaenge , 0        :'Spitze oben mitte
            '   Dreieckseite 3 = rechte Seite
            glColor3f  Farbe3Rot, Farbe3Gruen, Farbe3Blau
            glTexCoord2d 0.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'rechts unten
            glTexCoord2d 0.5, 1.0 : glVertex3f 0                , YLaenge , 0        :'Spitze oben mitte
            '   Dreieckseite 4 = Vorderseite
            glColor3f  Farbe4Rot, Farbe4Gruen, Farbe4Blau
            glTexCoord2d 0.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'rechts unten
            glTexCoord2d 0.5, 1.0 : glVertex3f 0                , YLaenge , 0        :'Spitze oben mitte
         glEnd

Das wars schon, jedem 3D-Punkt der Pyramidenteile
ist nun ein Punkt auf der Textur zugeordnet - fertig.

Jetzt müssen wir nur noch ins Hauptprogramm,
genauer gesagt, in die Schleife, wo die Pyramiden
aufgerufen werden, und dort jeweils das Texturieren
erlauben und ein Bild auf der Grafikkarte als Textur
festlegen.
Da wir beide Pyramiden direkt hintereinander aufrufen,
Schalten wir vor der ersten das Texturieren ein, und
nach der Zweiten dann erst wieder aus.
Da wir verschiedene Bilder für die beiden Pyramiden
zum Texturieren verwenden, müssen wir natürlich
direkt vor dem Aufruf der Pyramidensub jeweils
ein Bild aktivieren (klar, einmal Mauer, einmal Kirsche).

    Koordinatensystem("Anzeigen", "", 0, 0, 0) :' den Stringwert "Anzeigen" übergeben
    Schachbrettboden("BeschreibungsListeBoden", "", 0, 0, 0):' SchachbrettBoden in der 3D-Welt erstellen
    glPushMatrix                                                             :'akt.Position und Drehgrade sichern
    glEnable GL_TEXTURE_2D                    :'***********Texturieren erlauben*************
    glBindTexture GL_TEXTURE_2D, TexturNummer :'***********das erste Bild(mauer) als Textur aktivieren*************
    Pyramide("BeschreibungsListe"  , "", PyraXDrehw, PyraYDrehw, PyraZDrehw) :'die erste Pyramide
    glPopMatrix                                                              :'auf gesichterte Position und Drehgrade zurücksetzen
    glPushMatrix                                                             :'akt.Position und Drehgrade sichern
    glTranslatef -2, 0, 0                                                    :'hier der Verschiebebefehl, damit die 2.Pyramide woanderst steht
    glBindTexture GL_TEXTURE_2D, TexturZwei   :'***********das zweite Bild(Kirschen) als Textur aktivieren*************
    Pyramide("BeschreibungsListe"  , "", PyraXDrehw, PyraYDrehw, PyraZDrehw) :'und eine zweite Pyramide
    glPopMatrix                                                              :'auf gesichterte Position und Drehgrade zurücksetzen
    glDisable GL_TEXTURE_2D                    :'***********Texturieren wieder ausschalten*************

So, das hätten wir nun soweit, alle Codeschnippsel haben
wir besprochen. Wenn wir die nun alle in das Beispiel-
listing aus Tutorial 4 an den richtigen Stellen
einbauen, entsteht ein Programm, welches nach einigem
rumirren in unserer kleinen 3D-Welt folgendes Bild
anzeigt:
Ogl_tut_5_zwei_pyra_zwei_texturen
und wie gewohnt, hier zum Ende der Seite das komplette,
funktionsfähige Listing, mit allem was hier besprochen wurde:

'REM Tutorial 4 - OpenGL-Tutorial von Eastler
'-------------------------
'DIMs
'-------------------------
DIM SHARED AS STRING Tastendruck
DIM SHARED AS STRING Tlinks, Trechts, Tvor, Tzurueck, TCtrlVor, TCtrlZurueck, TCtrlLinks, TCtrlRechts
Tlinks       = CHR(255) & CHR( 75)  :' beim Drücken der Taste CursorLinks gibt die Tastatur CHR(255) & CHR( 75) zurück
Trechts      = CHR(255) & CHR( 77)  :' CursorRechts
Tvor         = CHR(255) & CHR( 72)  :' CursorHoch
Tzurueck     = CHR(255) & CHR( 80)  :' CursorRunter
TCtrlVor     = CHR(255) & CHR(141)  :'Ctrl oder STRG zusammen mit CursorHoch
TCtrlZurueck = CHR(255) & CHR(145)  :'Ctrl oder STRG zusammen mit CursorRunter
TCtrlLinks   = CHR(255) & CHR(115)  :'<--------CURSOR NACH LINKS-Variable mit Wert belegen
TCtrlRechts  = CHR(255) & CHR(116)  :'<--------CURSOR NACH RECHTS-Variable mit Wert belegenDIM AS SINGLE XRichtg, YRichtg, ZRichtg
DIM AS SINGLE XRichtg, YRichtg, ZRichtg
DIM AS SINGLE PyraXDrehw, PyraYDrehw, PyraZDrehw :'<------------------------ für Pyramide per Tasten drehen
DIM AS SINGLE WeltDrehX, WeltDrehY, WeltDrehZ
DIM AS INTEGER ConNr                :' Dateinummerspeicher beim Consolefenster öffnen
DIM AS UINTEGER TexturNummer, TexturZwei
DIM SHARED AS DOUBLE Pi
Pi = 3.14159265358979323846
'-------------------------
'Includes
'-------------------------
#include "fbgfx.bi"
#include once "GL/gl.bi"
#include once "GL/glu.bi"
#include once "vbcompat.bi" '<--------------um Zahlen per "format()" formatieren zu können

'-------------------------
'Declarationen
'-------------------------
DECLARE SUB Koordinatensystem(was AS STRING, TxtPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
DECLARE SUB Schachbrettboden(was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
DECLARE SUB Pyramide (was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
DECLARE FUNCTION BmpToOgl(BmpName AS STRING) AS INTEGER
DECLARE SUB Schreibe(Text AS STRING)
'-------------------------
' das Fenster öffnen
'-------------------------
screen 19, 32, , 2

'-------------------------
' Open-GL Init
'-------------------------
glViewport 0, 0, 800, 600                      ' den Current Viewport auf eine Ausgangsposition setzen
glMatrixMode GL_PROJECTION                     ' Den Matrix-Modus Projection wählen
glLoadIdentity                                 ' Diesen Modus auf Anfangswerte setzen
gluPerspective 45.0, 800.0/600.0, 0.1, 100.0   ' Grundeinstellungen des Anezeigefensters festlegen
glMatrixMode GL_MODELVIEW                      ' Auf den Matrix-Modus Modelview schalten
glLoadIdentity                                 ' und auch diesen auf Anfangswerte setzen
'TexturNummer = BmpToOgl("Mauer_128.bmp")                      ' Bitmapdatei als Textur an OpenGL schicken
TexturNummer = BmpToOgl("Mauer_128.bmp")       ' Bitmapdatei als Textur an OpenGL schicken
TexturZwei   =  BmpToOgl("Kirschen_256.bmp")   ' eine zweite Bitmapdatei als Textur an OpenGL schicken
glClearColor 0.5, 0.5, 0.50, 0.0               ' Setze Farbe für löschen auf Mittelgrau
glClearDepth 1.0                               ' Depth-Buffer Löschen erlauben
glEnable GL_DEPTH_TEST                         ' den Tiefentest GL_DEPTH_TEST einschalten
glClear GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT  'Tiefen- und Farbpufferbits löschen
'---------------------------
'HAUPTTEIL
'---------------------------
Schachbrettboden("SetzKantenLaenge", "", 0.5,  0,    0)
Schachbrettboden("SetzQuadsZahl",    "", 24,   24,   0)     :'6 Einheiten +und- = 12Einheiten Koordiantensystem, bei 0.5 Quadgröße 24 Stück
Schachbrettboden("SetzFarbe1",       "", 0,    0,    0.5)   :'erste Farbe dunkleres blau
Schachbrettboden("SetzFarbe2",       "", 0.25, 0.25, 0.25)  :'erste Farbe dunkles grau
Schachbrettboden("SetzStartPos",     "", -6,   -6,   -1)     :'ganz vorne ganz links beginnen, Boden auf Hoehe(3.Para)-1 verlegen(Y)

Pyramide("SetzLaengen" , "", 1, 1,  1)
Pyramide("SetzFarbe1"  , "", 1, 0, 0)
Pyramide("SetzFarbe2"  , "", 0, 1, 0)
Pyramide("SetzFarbe3"  , "", 0, 0, 1)
Pyramide("SetzFarbe4"  , "", 1, 1, 0)
Pyramide("SetzFarbe5"  , "", 1, 0, 1)

DO UNTIL Tastendruck = CHR(27)                         :'die Schleife solange immer wiederholen, bis in der Variablen Tastendruck die Esc-Taste (chr(27) steht
   Tastendruck = INKEY                                 :'Jeder Tastendruck wird sofort in die Variable Tastendruck gespeichert
   '---------------------------
   'ProgrammSchleife
   '---------------------------

   'JE NACH TASTENDRUCK DEN ENTSPRECHENDEN POSITIONSWERT VERÄNDERN
   SELECT CASE Tastendruck
      CASE "K", "k"
         Koordinatensystem("AnAus", "", 0, 0, 0) :' Schalter umstellen, falls An ist, auf Aus, sonst auf An
      CASE TCtrlrechts                :'Falls die Tasten "Cursor nach rechts" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         XRichtg = XRichtg - COS((WeltDrehY * Pi) / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg - SIN((WeltDrehY * Pi) / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TCtrllinks                 :'Falls die Tasten "Cursor nach rechts" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         XRichtg = XRichtg + COS((WeltDrehY * Pi) / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg + SIN((WeltDrehY * Pi) / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TCtrlzurueck               :'Falls die Tasten "Cursor unten" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         YRichtg = YRichtg + 0.04 :'HOCH und Runter bleibt unberührt von der Y-Drehung
      CASE TCtrlvor                   :'Falls die Tasten "Cursor hoch" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         YRichtg = YRichtg - 0.04 :'HOCH und Runter bleibt unberührt von der Y-Drehung
      CASE TZurueck                   :'Falls die Taste "Cursor runter" gedrückt wurde
         XRichtg = XRichtg + COS((WeltDrehY-90) * Pi / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg + SIN((WeltDrehY-90) * Pi / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TVor                       :'Falls die Taste "Cursor hoch" gedrückt wurde
         XRichtg = XRichtg + COS((WeltDrehY+90) * Pi / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg + SIN((WeltDrehY+90) * Pi / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TLinks                      :'Falls die Taste "Cursor links" gedrückt wurde
         WeltDrehY = WeltDrehY - 1
      CASE TRechts                     :'Falls die Taste "Cursor rechts" gedrückt wurde
         WeltDrehY = WeltDrehY + 1
      CASE "x"
         PyraXDrehw=PyraXDrehw+12
      CASE "X"
         PyraXDrehw=PyraXDrehw-12
      CASE "y"
         PyraYDrehw=PyraYDrehw+12
      CASE "Y"
         PyraYDrehw=PyraYDrehw-12
      CASE "z"
         PyraZDrehw=PyraZDrehw+12
      CASE "Z"
         PyraZDrehw=PyraZDrehw-12
      CASE "i", "I"                       :'<------------------------
         ConNr = FREEFILE                 :'nächste freie Dateinummer für öffnen von FreeBasic holen und in ConNr merken
         open "con" FOR OUTPUT AS #ConNr  :'und nun die Konsole als Datei unter Nr ConNr öffnen
            PRINT #ConNr, "PyraXDrehw:" & format(PyraXDrehw,"0.00") & "  PyraYDrehw:" & format(PyraYDrehw,"0.00") & "  PyraZDrehw:" & format(PyraZDrehw,"0.00")
            PRINT #ConNr, "XRichtg:" & format(XRichtg,"0.00") & "  YRichtg:" & format(YRichtg,"0.00") & "  ZRichtg:" & format(ZRichtg,"0.00")
         CLOSE #ConNr
   END SELECT   'PRÜFEN, DASS DIE POSITIONSWERTE IN ALLEN DREI RICHTUNGEN ZWISCHEN -3 und +3 BLEIBEN
   IF XRichtg >  6 THEN XRichtg =  6           :'falls zu weit rechts, bei  6 festnageln
   IF XRichtg < -6 THEN XRichtg = -6           :'falls zu weit links,  bei -6 festnageln
   IF YRichtg >  6 THEN YRichtg =  6           :'falls zu weit hoch,   bei  6festnageln
   IF YRichtg < -6 THEN YRichtg = -6           :'falls zu weit runter, bei -6 festnageln
   IF ZRichtg >  5 THEN ZRichtg =  5           :'falls zu weit zurück, bei 10 festnageln
   IF ZRichtg < -10 THEN ZRichtg = -10           :'falls zu weit vor,    bei -6 festnageln
   glClear GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT  :'bisherig erstellte Objekte löschen, unsere 3D-Welt wieder von Neuem an erstellen
   glPushMatrix                                        :'aktuelle Position + Drehgrade sichern (2.Zettel mit gleicher Pos auf PositionsSTACK)
    glRotatef WeltDrehY, 0, 1, 0                       :'Rotationsbefehl der Kamera, um Y ist zu drehen
    glTranslatef XRichtg, YRichtg, ZRichtg             :'Verschiebung der Kamera, auf neue Werte einstellen

    '-------------------------
    'AB HIER OBJEKTE ERSTELLEN
    '-------------------------
    glPointSize(5)                             :' Punktgröße auf 5 = deutlich groß
    glColor3f 1.0,0.0,0.0                      :' Anzeigefarbe auf ROT setzen
    Koordinatensystem("Anzeigen", "", 0, 0, 0) :' den Stringwert "Anzeigen" übergeben
    Schachbrettboden("BeschreibungsListeBoden", "", 0, 0, 0):' SchachbrettBoden in der 3D-Welt erstellen
    glPushMatrix                                                             :'akt.Position und Drehgrade sichern
    glEnable GL_TEXTURE_2D                    :'***********Texturieren erlauben*************
    glBindTexture GL_TEXTURE_2D, TexturNummer :'***********das erste Bild(mauer) als Textur aktivieren*************
    Pyramide("BeschreibungsListe"  , "", PyraXDrehw, PyraYDrehw, PyraZDrehw) :'die erste Pyramide
    glPopMatrix                                                              :'auf gesichterte Position und Drehgrade zurücksetzen
    glPushMatrix                                                             :'akt.Position und Drehgrade sichern
    glTranslatef -2, 0, 0                                                    :'hier der Verschiebebefehl, damit die 2.Pyramide woanderst steht
    glBindTexture GL_TEXTURE_2D, TexturZwei   :'***********das zweite Bild(Kirschen) als Textur aktivieren*************
    Pyramide("BeschreibungsListe"  , "", PyraXDrehw, PyraYDrehw, PyraZDrehw) :'und eine zweite Pyramide
    glPopMatrix                                                              :'auf gesichterte Position und Drehgrade zurücksetzen
    glDisable GL_TEXTURE_2D                    :'***********Texturieren wieder ausschalten*************

    '-----------------
    'OBJEKTE ERSTELLEN
    '-----------------

'-------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXX RECHTECK MIT TEXTUR ERZEUGEN XXXXXXXXXXXXXXXX
'-------------------------------------------------------------------
   glEnable GL_TEXTURE_2D          :'Texturieren mit dem grad aktiven Bild einschalten/erlauben
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL) :'texturieren "voll drauf" kein durchsichtig etc
   glBindTexture GL_TEXTURE_2D, TexturNummer      :'Unser Bild auf der Grafikkarte als aktives setzen
   glColor3f 1.0,0.0,0.0                          :'Anzeigefarbe (für das Rechteck) auf ROT setzen
   glBegin GL_QUADS                               :'Beginn Beschreibungsliste Objekt Viereck
      glTexCoord2d 0.0, 1.0 : glVertex3f -1.0,  1.0, -4.0  :'die linke obere Ecke des Rechtecks
      glTexCoord2d 0.0, 0.0 : glVertex3f -1.0, -1.0, -4.0  :'die linke untere Ecke des Rechtecks
      glTexCoord2d 1.0, 0.0 : glVertex3f +1.0, -1.0, -4.0  :'die rechte untere Ecke des Rechtecks
      glTexCoord2d 1.0, 1.0 : glVertex3f +1.0,  1.0, -4.0  :'die rechte obere Ecke des Rechtecks
   glEnd                                          :'Fertig mit dem Objekt
   glDisable GL_TEXTURE_2D         :'Texturieren ausschalten (es soll ja nur das Viereck texturiert werden, nicht die Pyramiden)
'-------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
'-------------------------------------------------------------------
   glEnable GL_TEXTURE_2D          :'Texturieren mit dem grad aktiven Bild einschalten/erlauben
   glBindTexture GL_TEXTURE_2D, TexturNummer      :'Unser Bild auf der Grafikkarte als aktives setzen
   glColor3f 1.0,0.0,0.0                          :'Anzeigefarbe (für das Rechteck) auf ROT setzen
   glBegin GL_QUADS                               :'Beginn Beschreibungsliste Objekt Viereck
      glTexCoord2d 0.0, 1.0 : glVertex3f -4.0,  1.0, -4.0  :'die linke obere Ecke des Rechtecks
      glTexCoord2d 0.0, 0.0 : glVertex3f -4.0, -1.0, -4.0  :'die linke untere Ecke des Rechtecks
      glTexCoord2d 1.0, 0.0 : glVertex3f -2.0, -1.0, -4.0  :'die rechte untere Ecke des Rechtecks
      glTexCoord2d 1.0, 1.0 : glVertex3f -2.0,  1.0, -4.0  :'die rechte obere Ecke des Rechtecks
   glEnd                                          :'Fertig mit dem Objekt
   glDisable GL_TEXTURE_2D         :'Texturieren ausschalten (es soll ja nur das Viereck texturiert werden, nicht die Pyramiden)
'-------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
'-------------------------------------------------------------------


    flip                                               :'liebes OpenGL, zeig alles, was in der Schleife für dich vornedran steht, auf Monitor an
   '---------------------------
   'Ende der Schleife
   '---------------------------
   glPopMatrix                                         :' Aufgabe erledigt, den zweiten Zettel mit der geänderten Pos wegschmeißen, dann ist Alte Pos wieder aktuelle Pos
LOOP

END

'--------------------------------
SUB Koordinatensystem(was AS STRING, TxtPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
   DIM AS INTEGER Zaehler
   STATIC AS INTEGER AnzeigeSchalter
   SELECT CASE UCASE(was)
      CASE "ANAUS"
         IF AnzeigeSchalter = 0 THEN AnzeigeSchalter = 1 ELSE AnzeigeSchalter = 0
      CASE "ANZEIGEN"
        IF AnzeigeSchalter = 1 THEN
         glBegin GL_LINES:' Ja, wir beginnen mit der Beschreibungsliste fuer Linien
         'und zwar zuerst die positiven Bereiche jeder Achse
         glColor3f 1.0, 1.0, 1.0        :' Positives Maß = weiße Teilstriche (Rot 1.0, blau 1.0, grün 1.0 = weiß)
         FOR Zaehler = 1 TO 6 :'fuer die Maßeinheitspositionen 1, 2 und 3 Striche Ziehen
            'X-ACHSE
               'senkrechter Strich (wie Y-Achse)
            glVertex3f  Zaehler+Para1,  0.2+Para2,  0.0+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1, -0.2+Para2,  0.0+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
               'waagerechter Strich (wie Z-Achse)
            glVertex3f  Zaehler+Para1,  0.0+Para2,  0.2+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1,  0.0+Para2, -0.2+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
            'Y-ACHSE
               'Strich wie X-Achse
            glVertex3f  0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach +X, Tiefe = 0
            glVertex3f -0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach -X, Tiefe = 0
               'Strich wie Z-Achse
            glVertex3f  0.0+Para1, Zaehler+Para2,  0.2+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach vorne/+Z, Seite (X) = 0
            glVertex3f  0.0+Para1, Zaehler+Para2, -0.2+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach hinten/-Z, Seite (X) = 0
            'Z-ACHSE
               'Strich waagerecht (X)
            glVertex3f  0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach rechts +X, Hoehe = 0
            glVertex3f -0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach links -X, Hoehe = 0
               'Strich senkreicht (Y)
            glVertex3f  0.0+Para1,  0.2+Para2,  Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach oben +Y, Seite = 0
            glVertex3f  0.0+Para1, -0.2+Para2,  Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach unten -Y, Seite = 0
         NEXT Zaehler
         glColor3f 0.0, 0.0, 0.0        :' Negatives Maß = schwrze Teilstriche (alles auf 0)
         FOR Zaehler = -6 TO -1 :'fuer die Maßeinheitspositionen -3, -2 und -1 Striche Ziehen
         'X-ACHSE
            'senkrechter Strich (wie Y-Achse)
            glVertex3f  Zaehler+Para1,  0.2+Para2,  0.0+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1, -0.2+Para2,  0.0+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
            'waagerechter Strich (wie Z-Achse)
            glVertex3f  Zaehler+Para1,  0.0+Para2,  0.2+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1,  0.0+Para2, -0.2+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
            'Y-ACHSE
               'Strich wie X-Achse
            glVertex3f  0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach +X, Tiefe = 0
            glVertex3f -0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach -X, Tiefe = 0
               'Strich wie Z-Achse
            glVertex3f  0.0+Para1, Zaehler+Para2,  0.2+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach vorne/+Z, Seite (X) = 0
            glVertex3f  0.0+Para1, Zaehler+Para2, -0.2+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach hinten/-Z, Seite (X) = 0
            'Z-ACHSE
               'Strich waagerecht (X)
            glVertex3f  0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach rechts +X, Hoehe = 0
            glVertex3f -0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach links -X, Hoehe = 0
               'Strich senkreicht (Y)
            glVertex3f  0.0+Para1,  0.2+Para2,  Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach oben +Y, Seite = 0
            glVertex3f  0.0+Para1, -0.2+Para2,  Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach unten -Y, Seite = 0
         NEXT Zaehler
         'UND NUN DIE DREI ACHSEN SELBST:
         'X-ACHSE
         glColor3f 1.0, 0.0, 0.0        :' Xachse = rot
         glVertex3f  -6.0+Para1, 0+Para2, 0+Para3
         glVertex3f  +6.0+Para1, 0+Para2, 0+Para3
         'Y-ACHSE
         glColor3f 0.0, 1.0, 0.0        :' Yachse = grün
         glVertex3f  0+Para1, -6.0+Para2, 0+Para3
         glVertex3f  0+Para1, +6.0+Para2, 0+Para3
         'Z-ACHSE
         glColor3f 0.0, 0.0, 1.0        :' Zachse = blau
         glVertex3f  0+Para1, 0+Para2, -6.0+Para3
         glVertex3f  0+Para1, 0+Para2, +6.0+Para3
         glEnd
       END IF
   END SELECT
END SUB
'-------------------------
SUB Schachbrettboden(was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
   STATIC AS SINGLE QuadKantenLaenge, QuadsQuer, QuadsTief
   STATIC AS SINGLE Farbe1Rot, Farbe1Gruen, Farbe1Blau
   STATIC AS SINGLE Farbe2Rot, Farbe2Gruen, Farbe2Blau
   STATIC AS SINGLE StartPosX, StartPosZ, BodenHoehe
   STATIC AS INTEGER ZaehlerX, ZaehlerZ                   :'ForNext-Zählvars, ggf. rekursiver aufruf dieser Sub, drum STATIC
   SELECT CASE UCASE(was)
      CASE "SETZKANTENLAENGE"
         'Aufruf hierfür in Para1 die Länge der Kanten der Quadrate
         QuadKantenLaenge = Para1
      CASE "SETZQUADSZAHL"
         'Aufruf hierfür in Para1 die Anzahl von Quads quer,
         'in Para2 die Anzahl in der Tiefenrichtung
         QuadsQuer = Para1
         QuadsTief = Para2
      CASE "SETZFARBE1"
         'in Para1 Para2 Para2 die Rot Grün und Blauwerte der ersten Quadratfarbe
         Farbe1Rot   = Para1
         Farbe1Gruen = Para2
         Farbe1Blau  = Para3
      CASE "SETZFARBE2"
         'in Para1 Para2 Para2 die Rot Grün und Blauwerte der ersten Quadratfarbe
         Farbe2Rot   = Para1
         Farbe2Gruen = Para2
         Farbe2Blau  = Para3
      CASE "SETZSTARTPOS"
         'in Para1 Para2 Para2 die X und Z Position, von wo begonnen wird,
         'die vielen Quadrate zu erstellen
         StartPosX   = Para1
         StartPosZ   = Para2
         BodenHoehe  = Para3
      CASE "BESCHREIBUNGSLISTEBODEN"
         'Hier erstellen wir die FOR-NEXT-SCHLEIFEN,
         'welche die OpenGL-Beschreibungsliste
         'zum Anzeigen des Bodens erstellen
         glBegin GL_QUADS
            FOR ZaehlerX = 1 TO QuadsQuer     :'Laufnr des grad zu zeichnenden Quads auf der X-Achse
               FOR ZaehlerZ = 1 TO QuadsTief  :'Laufnr des grad zu zeichnenden Quads auf der Z-Achse
                  'Die Farbe festlegen
                  IF ((ZaehlerX+ZaehlerZ)\2)*2=(ZaehlerX+ZaehlerZ) THEN
                     'Wenn die Summe von ZahlerX+ZaehlerY gerade ist Farbe1 nehmen
                     glColor3f Farbe1Rot, Farbe1Gruen, Farbe1Blau
                  ELSE
                     'Wenn die Summe von ZahlerX+ZaehlerY UNgerade ist Farbe2 nehmen
                     glColor3f Farbe2Rot, Farbe2Gruen, Farbe2Blau
                  END IF
                  'Die Eckpunkte der Quadrate benennen wir in der Reihenfolge GEGEN DEN UHRZEIGERSINN, von oben draufschauend
                  glVertex3f StartPosX+(ZaehlerX-1)*QuadKantenLaenge,  BodenHoehe,  StartPosZ+(ZaehlerZ-1)*QuadKantenLaenge
                  glVertex3f StartPosX+ ZaehlerX   *QuadKantenLaenge,  BodenHoehe,  StartPosZ+(ZaehlerZ-1)*QuadKantenLaenge
                  glVertex3f StartPosX+ ZaehlerX   *QuadKantenLaenge,  BodenHoehe,  StartPosZ+ ZaehlerZ   *QuadKantenLaenge
                  glVertex3f StartPosX+(ZaehlerX-1)*QuadKantenLaenge,  BodenHoehe,  StartPosZ+ ZaehlerZ   *QuadKantenLaenge
               NEXT ZaehlerZ
            NEXT ZaehlerX
         glEnd
      CASE ELSE
         'Hier kommen alle SUB-Aufrufe an, welche als
         'was-Parameter einen Eintrag haben, der hier
         'nicht ausgewertet wurde.
         'Tippfehler vom Programmierer????
   END SELECT
END SUB
'-------------------------
SUB Pyramide(was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
   'Pyramide erstellen, Grundfläche/Quadrat = auf Höhe 0, seitlich mittig auf X- und Z-Achse Positioniert
   'Grundflächengroesse = XLaenge x ZLaenge, Höhe Pyramide = ZLaenge
   STATIC AS SINGLE XLaenge, YLaenge, ZLaenge
   STATIC AS SINGLE Farbe1Rot, Farbe1Gruen, Farbe1Blau
   STATIC AS SINGLE Farbe2Rot, Farbe2Gruen, Farbe2Blau
   STATIC AS SINGLE Farbe3Rot, Farbe3Gruen, Farbe3Blau
   STATIC AS SINGLE Farbe4Rot, Farbe4Gruen, Farbe4Blau
   STATIC AS SINGLE Farbe5Rot, Farbe5Gruen, Farbe5Blau
   STATIC AS INTEGER ZaehlerX, ZaehlerZ                   :'ForNext-Zählvars, ggf. rekursiver aufruf dieser Sub, drum STATIC
   SELECT CASE UCASE(was)
      CASE "SETZLAENGEN"  : XLaenge   = Para1 : YLaenge     = Para2 : ZLaenge     = Para3
      CASE "SETZFARBE1"   : Farbe1Rot = Para1 : Farbe1Gruen = Para2 : Farbe1Blau  = Para3 :'ein Dreieck
      CASE "SETZFARBE2"   : Farbe2Rot = Para1 : Farbe2Gruen = Para2 : Farbe2Blau  = Para3 :'ein Dreieck
      CASE "SETZFARBE3"   : Farbe3Rot = Para1 : Farbe3Gruen = Para2 : Farbe3Blau  = Para3 :'ein Dreieck
      CASE "SETZFARBE4"   : Farbe4Rot = Para1 : Farbe4Gruen = Para2 : Farbe4Blau  = Para3 :'ein Dreieck
      CASE "SETZFARBE5"   : Farbe5Rot = Para1 : Farbe5Gruen = Para2 : Farbe5Blau  = Para3 :'der Boden/Quadrat
      CASE "BESCHREIBUNGSLISTE"
         glrotatef Para1, 1, 0, 0    :'<----------- um X-Achse drehen
         glrotatef Para2, 0, 1, 0    :'<----------- um Y-Achse drehen
         glrotatef Para3, 0, 0, 1    :'<----------- um Z-Achse drehen
         'die OpenGL-Beschreibungsliste zum Anzeigen der Pyramide erstellen
         glBegin GL_QUADS
            'der Boden der Pyramide als Quadrat, auf Höhe 0(Y-Achse),
            'seitliche Ausrichtungen = Quadratmitte = X-Achsenmitte und Z-Achsenmitte
            'damit Zentriert sitzt, für 1.Punkt einfach halbe Kantenlänge von 0 Abziehen, für 2. dazuaddieren
            'Reihenfolge Eckpunktangabe gegen Uhrzeigersinn VON UNTEN her gesehen (unten=Außenseite später)
            glColor3f  Farbe5Rot, Farbe5Gruen, Farbe5Blau :'falls Texturieren Disabled ist, wird diese Farbe genommen
            glTexCoord2d 0.0, 1.0 : glVertex3f 0-(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'Ecke links oben
            glTexCoord2d 0.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'Ecke links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'Ecke rechts unten
            glTexCoord2d 1.0, 1.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'Ecke rechts oben
         glEnd
         glBegin GL_TRIANGLES
            '   Dreieckseite 1 = linke Seite
            glColor3f  Farbe1Rot, Farbe1Gruen, Farbe1Blau
            glTexCoord2d 0.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0       , 0-(ZLaenge/2) :'links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0       , 0+(ZLaenge/2) :'rechts unten
            glTexCoord2d 0.1, 1.0 : glVertex3f 0                , YLaenge , 0             :'Spitze oben
            '   Dreieckseite 2 = hintere Seite /Rückseite
            glColor3f  Farbe2Rot, Farbe2Gruen, Farbe2Blau
            glTexCoord2d 0.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'rechts unten
            glTexCoord2d 0.5, 1.0 : glVertex3f 0                , YLaenge , 0        :'Spitze oben mitte
            '   Dreieckseite 3 = rechte Seite
            glColor3f  Farbe3Rot, Farbe3Gruen, Farbe3Blau
            glTexCoord2d 0.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'rechts unten
            glTexCoord2d 0.5, 1.0 : glVertex3f 0                , YLaenge , 0        :'Spitze oben mitte
            '   Dreieckseite 4 = Vorderseite
            glColor3f  Farbe4Rot, Farbe4Gruen, Farbe4Blau
            glTexCoord2d 0.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'rechts unten
            glTexCoord2d 0.5, 1.0 : glVertex3f 0                , YLaenge , 0        :'Spitze oben mitte
         glEnd
      CASE ELSE
         'Hier kommen alle SUB-Aufrufe an, welche als
         'was-Parameter einen Eintrag haben, der hier
         'nicht ausgewertet wurde.
         'Tippfehler vom Programmierer????
   END SELECT
END SUB
'--------------------------------
FUNCTION BmpToOgl(BmpName AS STRING) AS INTEGER
   DIM AS BYTE PTR DateiImRamPtr, SpBmpPixelZeiger
   DIM AS INTEGER DateiNummer, Ganzzahl, Breite, Hoehe
   DIM AS UINTEGER TexturNummer, RotWert, BlauWert, ZeilenZaehler, PunktZaehler
   DIM AS UBYTE Buchstabe
   DIM AS STRING Dummy
   'Von Festplatte in den RAM
   DateiNummer = FREEFILE
   OPEN BmpName FOR INPUT AS #DateiNummer
   IF LOF(DateiNummer) = 0 THEN
      'BildDatei Existiert nicht
      Schreibe("Die Bilddatei " & BmpName & " ist nicht vorhanden oder hat Laenge 0! breche ab")
      CLOSE #DateiNummer
      END
   ELSE
      'BildDatei ist vorhanden, String aus den ersten zwei Bytes in der Datei basteln
      GET #DateiNummer, 1, Buchstabe
      Dummy=CHR(Buchstabe)
      GET #DateiNummer, 2, Buchstabe
      Dummy = Dummy + CHR(Buchstabe)
      IF Dummy <> "BM" THEN
         'keine Bitmap-Datei
         Schreibe("Die Bilddatei " & BmpName & " ist keine Bitmapdatei! Kennung=" & Dummy & " breche ab")
         CLOSE #DateiNummer
         END
      ELSE
         'Datei ist Bitmap, prüfen ob komprimiert
         GET #DateiNummer, 31, Ganzzahl
         IF Ganzzahl <> 0 THEN
            'Bitampdatei ist komprimiert
            Schreibe("Die Bilddatei " & BmpName & " ist komprimiert gespeichert, kann damit nicht umgehen, breche ab!")
            CLOSE #DateiNummer
            END
         ELSE
            'unkomprimierte Bitmap-Datei, prüfen ob quadratisch und 64/128/256 bytes kantenlänge
            GET #DateiNummer, 19, Breite
            GET #DateiNummer, 23, Hoehe
            IF (Breite <> Hoehe) OR (Breite <> 64 AND Breite <> 128 AND Breite <> 256) THEN
               'Bild ist entweder nicht quadratisch oder nicht 64/128/256er Kantenlängen
               Schreibe("Die Bilddatei " & BmpName & " ist entweder nicht Quadratisch oder hat keine Kantenlänge von 64, 128 oder 256 Pixel")
               Schreibe("       Breite:" & Breite & " Hoehe:" & Hoehe & "   breche ab!")
               CLOSE #DateiNummer
               END
            ELSE
               '***************************************
               'Bilddatei ist OK, kann verwendet werden
               '***************************************
               CLOSE #DateiNummer
               DateiImRamPtr = IMAGECREATE(Breite, Hoehe, 0, 32)
               BLOAD(BmpName, DateiImRamPtr)
            END IF
         END IF
      END IF
   END IF
   'Entweder das Programm hat abgebrochen, oder die Datei ist OK und das Bild steht jetzt im Ram an Adresse DateiImRamPtr
   SpBmpPixelZeiger = DateiImRamPtr + Len(FB.IMAGE)
   FOR ZeilenZaehler = 0 TO Hoehe-1
      FOR PunktZaehler = 0 TO Breite-1
         BlauWert = SpBmpPixelZeiger[0]        :'aus Ram das Byte mit dem Blauwert des grad zu bearbeitenden Pixels holen
         RotWert  = SpBmpPixelZeiger[2]        :'aus Ram das Byte mit dem Rotwert des grad zu bearbeitenden Pixels holen
         SpBmpPixelZeiger[0] = RotWert         :'den Rotwert an die Stelle des Blauwertes im RAM schreiben
         SpBmpPixelZeiger[2] = BlauWert        :'den Blauwert an die Stelle des Rotwertes im RAM schreiben
         SpBmpPixelZeiger = SpBmpPixelZeiger+4 :'Ram-Adresse um 4 bytes erhöhen = Start Pixeldaten des nächsten Pixels
      NEXT PunktZaehler
   NEXT ZeilenZaehler
   'Farbtiefe des OpenGL-Fensters holen
   DIM AS INTEGER FensterFarbTiefe
   SCREENCONTROL 5, FensterFarbTiefe :' 5 = GET_SCREEN_DEPTH : Farbtiefe in Bits des OpenGL-Fensters ermitteln
   'XXXXXXXXXXXXXXXXXXXXX UND RAM-BILD AN OPENGL ÜBERGEBRN XXXXXXXXXXXXXXXX
   glGenTextures 1, @TexturNummer
   glBindTexture GL_TEXTURE_2D, TexturNummer
   IF FensterFarbTiefe = 32 OR FensterFarbTiefe = 24 THEN
      glTexImage2D GL_TEXTURE_2D, 0, GL_RGBA, Breite, Hoehe, 0, GL_RGBA, GL_UNSIGNED_BYTE, DateiImRamPtr+Len(FB.IMAGE)
   ELSE
      glTexImage2D GL_TEXTURE_2D, 0, GL_RGB, Breite, Hoehe, 0, GL_RGBA, GL_UNSIGNED_BYTE, DateiImRamPtr+Len(FB.IMAGE)
   END IF
   glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR
   glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR
   ImageDestroy(DateiImRamPtr)
   BmpToOgl=TexturNummer
END FUNCTION
'-------------------
SUB Schreibe(Text AS STRING)
   DIM AS INTEGER DateiNummer
   'AUF DEN MONITOR IN DIE TEXTCONSOLE
   DateiNummer=FREEFILE
   OPEN "CONS" FOR OUTPUT AS #DateiNummer
   PRINT #DateiNummer, Text
   CLOSE #DateiNummer
   'NOCHMAL IN EINE DATEI
   DateiNummer=FREEFILE
   OPEN "Protokoll.txt" FOR APPEND AS #DateiNummer
   PRINT #DateiNummer, DATE & " " & TIME & " " & Text
   CLOSE #DateiNummer
END SUB

[seitenwechsel]
So, nun sind wir in der Lage, unkomprimierte Bitmap-Dateien
einzulesen, an OpenGL zu geben und einzusetzen.

Damit kann man schon einiges anfangen. Wenn Sie dann aber
Ihr superduper-OpenGL-Programm fertigstellen und veröffentlichen
wollen, müssen Sie sämtliche Texturen und Bilder im Bitmap-Format
dem Programm beilegen.
Wenn Sie dabei Ihr Programm samt Bilder in ein Zip-Archiv packen,
ist das Ganze nicht sooo sehr groß, aber beim Anwender auf der
Festplatte muß das alles in wieder entpackter Form stehen und
das kann schon ettlichen Speicherplatz belegen.
Gleichzeitig können Sie über Bitmap-Dateien nicht mit dem
Alpha-Kanal arbeiten, welcher für Durchsichtigkeit einer Textur/
eines Bildes zuständig ist, weil in Bmp-Dateien eben kein
Alphakanal abgespeichert werden kann.

Deshalb hab ich mir überlegt, nachdem wir nun bis ins Detail
wissen, wie es geht, möchten wir vielleicht doch auch mit
anderen Bildformaten als Dateiquelle arbeiten.
Nach einigem Suchen und rumprobieren konnte ich feststellen,
daß dieses "Rad" bereits erfunden ist, ich also nicht nochmal
alles erarbeiten brauche.

Am idealsten erscheint hierfür das Arbeiten mit der
Lib FreeImage, welche fast sämtliche Bildtypen einlesen kann,
und diese eingelesenen Bilder dann alle in einem gleichen
Format im Speicher stehen, sodaß wir im Grunde mit einer
einzigen Einleseroutine in den Ram arbeiten, dann mit einer
einzigen Umsetzung für OpenGL und ebenfalls einer einzigen
Übergaberoutine an OpenGL.

Soweit ich das recherchieren konnte, ist FreeImage in der Lage,
folgende Bilddateien von der Festplatte in den Arbeitsspeicher
einzulesen:
Bitmap, Ico, Jpeg, Jng, Koala, Iff/Lbm, Mng, Pbm, PbmRaw, Pcd,
Pcx, Pgm, PgmRaw, Png, Ppm, ppmRaw, Ras, Targa, Tiff, WBmp,
Psd, Cut, Xbm, Xpm, dds, Gif, Hdr, FaxG3, Sgi, Exr, J2k, Jp2.

Na, ist das Nichts? Ich muß zugeben, einige der Formate kenne
ich auch nur vom Hören-Sagen, in der Hand hatte ich manche
der Formate auch noch nie ;-).

Dann gehen wir es mal an, für unsere bisherige Routine zum
Bmp-Dateien einlesen eine Ersatzroutine zu erstellen, mit der
wir die oben angeführten Formate als Quelle für Texturen
verwenden können.
[seitenwechsel]

Eine Laderoutine für alle Dateiformate per FreeImage

Vorweg erst mal ein rießiges Dankeschön an csde_rats,
er hat mit seinem Tutorial die eigentliche Arbeit
geleistet.
Ursprünglich wollte ich auf andere Formate als Bmp
nicht eingehen, nur weil csde_rats die Arbeit mit
seinem Tutorial schon gemacht hatte, konnte ich hier
ohne größeren Aufwand die FreeImage-Lib einbinden.

Bedingungen

Falls Sie unter Windows arbeiten, sollten Sie
zuerst sicherstellen, daß beim Installieren von
FreeBasic die Library FreeImage mit installiert wurde.
Im FreeBasic-Paket V. 0.20 ist FreeImage von Haus
aus angehakt, wird also mit installiert.
Prüfen Sie einfach, ob in Ihrem Verzeichnis:
...FreeBasic/lib/win32/
die Datei libFreeImage.dll.a vorhanden ist.

ACHTUNG! In der FreeBasic-Version 0.20b stimmt irgend eine Kleinigkeit im Zusammenspiel mit FreeImage (noch) nicht! Deshalb sollten Sie hier auf eine erprobte und funktionierende FreeImage-Version umstellen! UNTER WINDOWS GEHT DAS WIE FOLGT: Ein Forumsthema dazu finden Sie unter: Externer Link!http://forum.qbasic.at/viewtopic.php?t=5819 darin ist der Downloadlink für die FreeImage-Version 3.9.3 unter http://www.freebasic-portal.de/index.php?s=download&id=89 angegeben. Damit erhalten Sie ein Zip-Archiv mit dem Namen freeimg393.zip. Entpacken Sie dieses Archiv irgendwohin, kopieren Sie die Datei: libFreeImage393.a innerhalb der FreeBasic-Programmstruktur unter: ...FreeBasic/lib/win32/libFreeImage393.a und die Datei: FreeImage393.bi ebenfalls innerhalb der FreeBasic-Programmstruktur unter: ...FreeBasic/inc/FreeImage393.bi Damit bleibt die eigentliche Einrichtung der FreeImage-Lib unter FreeBasic 0.20b unberührt, zusätzlich steht nun aber auch die FreeImage-Lib in der Version 3.9.3 zur Verfügung. Es reicht, wenn Sie in Ihrem Programm beim Includen der Library einfach schreiben: #include "FreeImage393.bi" So mache ich das hier im Tutorial auch. Wenn es mit einer späteren FreeBasic-Version dann besser läuft, brauchen Sie nur die "393" aus dem Include-Befehl herausnehmen, dann arbeitet Ihr Programm mit der neueren Version, welche mit FreeBasic mitgeliefert wird. Denken Sie auch an die DLL-Datei, welche Sie Ihrem Programm dazugeben müssen, für die 3.9.3er-Version liegt diese im oben benannten Zip-Archiv mit drin. Bei Neueren FreeBasic-Versionen müssen Sie diese von der FreeImage-Internetseite in eben dieser neueren Version herunterladen. UNTER LINUX testen Sie am besten, ob die in Ihrem System installierte FreeImage-Version läuft. Schätze mal, daß es dort keine Probleme geben wird. Falls doch, die Version 3.9.3 läuft ;-)

Wenn Sie Linux als Betriebssystem verwenden,
kann FreeBasic beim Installieren nichts mitbringen,
Sie müssen für Ihre Linux-Version selbst nach einer
FreeImage-Libraray suchen und installieren, falls
diese nicht serienmäßig in Ihrer Distribution
bereits enthalten ist.

Ebenfalls ist es unter Linux notwendig, daß der
Endanwender Ihres Programmes diese FreeImage-Lib
in seinem System installiert hat.

Diese Bedingungen sollten in Ihr neues OpenGL-Projekt
als Entscheidungsgrundlagen mit einfließen.
Falls Sie Ihr neues OpenGL-Projekt auch unter Linux
herausgeben wollen, und Sie mit wenigen Bildern
arbeiten können, ist es für den Linux-Endanwender
einfacher, etwas mehr an Festplattenplatz durch die
Bitmapbilder zu verbrauchen, als für sein Linuxsystem
eine FreeImage-Library zu suchen.
Falls Sie jedoch mit dem Alpha-Kanal, sprich Transparenz
beim Texturieren arbeiten, gehts ja nicht anders, dann
müssen Sie ja in anderen Dateiformaten als Bitmap
die Bilder beilegen, um den Alphakanal mit im Bild
abspeichern zu können.
Und damit sind Sie fast schon gezwungen, mit einer
Library die Dateien einzulesen.
Aber wie gesagt, unter Windows "no Problem", auf Ihrem
kompilierenden System die FreeImage-Lib drauf, dann
steht alles, was der Anwender braucht, in der Exe gleich
mit drin, unter Windows muß der Endanwender also nur
die FreeImage.DLL im selben Verzeichnis haben, in
dem Ihr Exe-Programm steht. Und diese DLL geben Sie
doch bestimmt gleich mit dazu.
Jedoch, beachten Sie die Nutzungsbedingungen der
FreeImage-Lib. Als Open-Source muß der Disclaimer
als Textdatei mit beigelegt werden und in Ihrem
Programm z.B. unter "Hilfe" ein Hinweis, daß eben
die Open-Source FreeImage-Lib in der Version 3.9.9
im Programm enthalten ist.

Die Function dafür erstellen

Als erstes müssen wir die Library FreeImage in unser
Program einbinden, das geschieht ganz einfach mit:
#include "FreeImage393.bi"
(Beachten Sie den Hinweis am Anfang dieser Seite,
mit der FreeImage-Version 3.9.3 zu arbeiten)

Und schon können wir uns darum kümmern, die Function
zu erstellen. Als Namen schlage ich einen deutlichen,
aussagekräftigen vor: BildToOgl:
DECLARE FUNCTION BildToOgl(BildDateiName AS STRING) AS INTEGER
Wie gesagt, wir übergeben den Dateinamen, deshalb
kriegt die Function als Parameter eine Stringvariable,
in der eben dieser Dateiname rüber gegeben wir,
zurück kommt ein Name des Bildes bei OpenGL, welcher
eine IntegerWert darstellt, entsprechend geben wir
der Function und damit auch dem Rückgabewert den
Typ UInteger.

Nun die Function selbst:
FUNCTION BildToOgl(BildDateiName AS STRING) AS INTEGER
'...hier alles weitere
END FUNCTION


und innerhalb der Sub dann, die Programmfolge, daß die
Sub die gewählte Datei von der Festplatte in den
Arbeitesspeicher holt.

Um FreeImage vor dem Fall zu bewahren, daß die Bilddatei
gar nicht existiert, prüfen wir dies mit FreeBasic:

FUNCTION BildToOgl(BildDateiName AS STRING) AS INTEGER
   '"Lade Bilddatei von Festplatte in FreeImageRam"
   DIM AS UINTEGER TexturNummer, Breite, Hoehe, ZeilenZaehler, PunktZaehler, BlauWert, RotWert, DateiNummer
   DIM AS FIBITMAP Ptr BildZeiger
   DIM AS ZSTRING PTR DateiNamenZeiger
   DIM AS FREE_IMAGE_FORMAT Ergebnis
   DIM DateiName AS ZSTRING * 512
   DateiName=LEFT(BildDateiName, 512)
   'mit FreeBasic checken, ob die Datei überhaupt existiert
   DateiNummer = FREEFILE
   OPEN DateiName FOR INPUT AS #DateiNummer
      IF LOF(DateiNummer) = 0 THEN
         SCHREIBE("DateiName Läenge=" & LEN(DateiName) & "=" & LEFT(DateiName, 20) & " Dateigroeße=" & LOF(DateiNummer) )
         END
      END IF
   CLOSE #DateiNummer

Da in der FreeImage-Lib beim Laden der Bildtyp anzugeben
ist, nutzen wir die ebenfalls in dieser Lib enthaltenen
Functionen, um eine Datei auf deren Bildtyp zu untersuchen
(sich auf die Endung des Dateinamens zu verlassen, ist
gefährlich, mir sind schon ettliche Bilddateien unter
gekommen, die Bmp heißen, und Jpg enthalten!)
FreeImage_GetFileType(filename AS STRING PTR, FI_DEFAULT(0) AS INTEGER)
Diese Funktion liefert einen Zahlenwert zurück, der für
den Bildtyp wie folgt steht:

'Auszug aus FreeImage.bi:
enum FREE_IMAGE_FORMAT
   FIF_UNKNOWN = -1
   FIF_BMP = 0
   FIF_ICO = 1
   FIF_JPEG = 2
   FIF_JNG = 3
   FIF_KOALA = 4
   FIF_LBM = 5
   FIF_IFF = FIF_LBM
   FIF_MNG = 6
   FIF_PBM = 7
   FIF_PBMRAW = 8
   FIF_PCD = 9
   FIF_PCX = 10
   FIF_PGM = 11
   FIF_PGMRAW = 12
   FIF_PNG = 13
   FIF_PPM = 14
   FIF_PPMRAW = 15
   FIF_RAS = 16
   FIF_TARGA = 17
   FIF_TIFF = 18
   FIF_WBMP = 19
   FIF_PSD = 20
   FIF_CUT = 21
   FIF_XBM = 22
   FIF_XPM = 23
   FIF_DDS = 24
   FIF_GIF = 25
   FIF_HDR = 26
   FIF_FAXG3 = 27
   FIF_SGI = 28
   FIF_EXR = 29
   FIF_J2K = 30
   FIF_JP2 = 31
end enum

Jedoch gibt es ein paar Bilddateien, welche mit dieser Funktion
nicht ermittelt werden können - FreeImage_GetFileTyp ließt bis
zu 16 Bytes aus der Datei um den Typ zu ermitteln. Die Ausnahmen
sind jene Dateitypen, die keinen Vorspann haben, oder die einen
solchen Infoblock am Ende der Datei enthalten, das sind:
z.B. CUT, MNG, PCD, TARGA und WBMP

Also checken wir die Rückantwort von FreeImage_GetFileTyp,
ob ein Wert gößer/gleich Null drin steht = ob die Datei erkannt wurde.
Falls -1 drin steht (unbekannter Typ), lassen wir eine ausführlichere
und langsamere Funktion auf die Datei los:
FreeImage_GetFIFFromFilename(Dateiname AS STRING PTR)
Diese Function geht einen anderen Weg in der Analyse und
liefert zurück, was immer auch gefunden wird.
Also nicht nur Dateitypen, die FreeImage laden kann, auch
andere Typen.
Darum müssen wir hinterher vor dem Laden prüfen, ob
FreeImage für den evtl. erst in der zweiten Variante
gefundenen Dateityp eine Laderoutine kennt:
IF FreeImage_FIFSupportsReading(fif)

Also nochmal wiederholend im Kurzformat:
mit ERGEBNIS = FreeImage_GetFileType(filename AS STRING PTR, FI_DEFAULT(0) AS INTEGER) die Datei prüfen,
falls dabei -1 zurückgegeben wird, mit ERGEBNIS = FreeImage_GetFIFFromFilename(Dateiname AS STRING PTR)
tiefer prüfen, um danach,
wenn ERGEBNIS ungleich -1 ist und FreeImage_FIFSupportsReading(fif) wahr ist
die Datei in den (FreeImage-)Ram laden.

Zum Laden verwenden wir den Befehl:
FIBITMAP(AS FIBITMAP PTR) = FreeImage_Load(
     ERGEBNIS(=ermittelter Formatwert nach obiger Liste) AS FREE_IMAGE_FORMAT,
     Dateiname AS STRING PTR,
     FormAttribut (=0)
      )


somit sieht das ganze Kontstrukt für den Teil

   Ergebnis = FIF_UNKNOWN :' Erst mal "Dateityp Unbekannt" reinsetzen, FIF_UNKNOWN = -1
   'Dateityp ermitteln:
   Ergebnis = FreeImage_GetFileType( @DateiName, 0)
   IF Ergebnis = FIF_UNKNOWN THEN
      SCHREIBE("erster Test mit " & DateiName & " schlug fehl, nehme Zweiten!")
      'mit der schnellen Routine kann das Dateiformat nicht ermittelt werden,
      'über die tiefer gehende Routine den Typ ermitteln
      Ergebnis = FreeImage_GetFIFFromFilename( @DateiName )
      IF Ergebnis <> FIF_UNKNOWN AND FreeImage_FIFSupportsReading(Ergebnis) THEN
         BildZeiger = FreeImage_Load( Ergebnis, @DateiName, 0 )
      ELSE
         'Auch die tiefergehende Routine schlägt fehl, Bild einlesen geht nicht!
         Schreibe("kann die Datei " & DateiName & " nicht einlesen, breche ab")
         END
      END IF
   ELSE
      BildZeiger = FreeImage_Load( Ergebnis, @DateiName, 0 )
   END IF
   'Nun steht das Bild im FreeImageFormat im Ram an Adresse BildZeiger

Dann wollten wir grundsätzlich im RAM mit 32-bit Farbtiefe
erstellen, damit wir bei der Übergabe an OpenGL immer von
der gleichen Voraussetzung ausgehen können.
Auch dafür hat FreeImage eine Funktion drin, die jegliches
Bildformat in 32-bit Farbtiefe umwandelt. Jedoch landet
das Ergebnis an einer anderen Speicherstelle, für die
wir einen Pointerwert erhalten. Also brauchen wir dazu
eine zweite Variable AS FIBITMAP Ptr, um uns diese Adresse
im Ram merken zu können:

   'eine Kopie davon mit 32-bit Farbtiefe erstellen
   DIM AS FIBITMAP Ptr BildZeiger32bit
   BildZeiger32bit = FreeImage_ConvertTo32Bits( BildZeiger )

Damit stehen die Bilddaten so, wir wir das brauchen,
also gehen wir an den Abschnitt "Aus RAM an OpenGL".

FreeImage_GetBits liefert uns einen Pointer
auf die eigentlichen Pixeldaten im Arbeitsspeicher,
und damit hätten wir dann alles, was wir für die
Übergabe an OpenGL bräuchten. Jedoch müssen wir beim
Farben umsetzen den Pointer "hochzählen", also den
Wert im verändern, obwohl wir den originalen Wert
später auch nochmal brauchen. Deshalb erstellen wir
zwei Pointer-Variablen, die die Adresse der Pixeldaten
enthalten:

   DIM AS BYTE PTR PixelImRam, RamPixelKopie
   PixelImRam = FreeImage_GetBits( BildZeiger32bit )
   RamPixelKopie = PixelImRam

Als Nächstes brauchen wir die Höhe/Breite des Bildes
in Pixel:

   'Bildbreite/Hoehe in Pixel holen
    Breite = FreeImage_GetWidth(BildZeiger32bit)
    Hoehe = FreeImage_GetHeight(BildZeiger32bit)

An diese Stelle können wir nun prüfen, ob das Bild
dem Format entspricht, was OpenGL für Texturen
verlangt.
Da wir hier bereits mit OpenGL 1.0 arbeiten wollen,
dürfen wir nur quadratische Bilder (Breite=Hoehe)
verwenden, und die Kantenlängen dürfen nur
entweder 64 oder 128 oder 256 Pixel lang sein:

   IF (Breite <> Hoehe) OR (Breite <> 64 AND Breite <> 128 AND Breite <> 256) THEN
      'Das Bild kann nicht als Textur für OpenGL 1.0 verwendet werden
      SCHREIBE("Das Bild " & DateiName & "kann nicht als Textur für OpenGL 1.0 verwendet werden")
      SCHREIBE("           Das Bild ist entweder nicht quadratisch, oder ")
      SCHREIBE("           die Seitenlängen sind nicht 64, 128 oder 256 Pixel!")
      SCHREIBE("           Breche ab!")
      'Ramspeicher von FreeImage freigeben
      FreeImage_Unload(BildZeiger)
      FreeImage_Unload(BildZeiger32bit)
      END
   END IF

In der vorigen Routine für Bitmap-Dateien hatten wir ja
bereits das Problem, daß die Farbwerte für Blau und Rot
vertauscht im Speicher stehen, das ist hier ebenfalls
so. Deshalb mit genau dem selben Konstrukt müssen wir
diese beiden Farbwerte austauschen:

   'ROT UND BLAU TAUSCHEN
   FOR ZeilenZaehler = 0 TO Hoehe-1
      FOR PunktZaehler = 0 TO Breite-1
         BlauWert = RamPixelKopie[0]        :'aus Ram das Byte mit dem Blauwert des grad zu bearbeitenden Pixels holen
         RotWert  = RamPixelKopie[2]        :'aus Ram das Byte mit dem Rotwert des grad zu bearbeitenden Pixels holen
         RamPixelKopie[0] = RotWert         :'den Rotwert an die Stelle des Blauwertes im RAM schreiben
         RamPixelKopie[2] = BlauWert        :'den Blauwert an die Stelle des Rotwertes im RAM schreiben
         RamPixelKopie = RamPixelKopie+4 :'Ram-Adresse um 4 bytes erhöhen = Start Pixeldaten des nächsten Pixels
      NEXT PunktZaehler
   NEXT ZeilenZaehler

Und jetzt genau so, wie in der vorigen reinen BMP-Routine,
schauen, mit welcher Tiefe unser OpenGL-Fenster erstellt
wurde:

   'Farbtiefe des OpenGL-Fensters holen
   DIM AS INTEGER FensterFarbTiefe
   SCREENCONTROL 5, FensterFarbTiefe :' 5 = GET_SCREEN_DEPTH : Farbtiefe in Bits des OpenGL-Fensters ermitteln

Und nun der Abschnitt, in dem das RamBild Pixel für Pixel
von OpenGL auf die Grafikkarte geholt wird.
Das Einzige, was sich gegenüber der Bmp-Routine ändert,
ist die Angabe, wo die Pixel im Ram stehen, diesmal
haben wir ja andere Variablen dafür:

   'XXXXXXXXXXXXXXXXXXXXX UND RAM-BILD AN OPENGL ÜBERGEBRN XXXXXXXXXXXXXXXX
   glGenTextures 1, @TexturNummer
   glBindTexture GL_TEXTURE_2D, TexturNummer
   IF FensterFarbTiefe = 32 OR FensterFarbTiefe = 24 THEN
      glTexImage2D GL_TEXTURE_2D, 0, GL_RGBA, Breite, Hoehe, 0, GL_RGBA, GL_UNSIGNED_BYTE, PixelImRam
   ELSE
      glTexImage2D GL_TEXTURE_2D, 0, GL_RGB, Breite, Hoehe, 0, GL_RGBA, GL_UNSIGNED_BYTE, PixelImRam
   END IF
   glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR
   glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR

Zum Schluß wieder aufräumen. Die Bilder bei FreeImage
werden nicht mehr gebraucht, da OpenGL sich die Daten
nun auf die Grafikkarte kopiert hat.

   FreeImage_Unload(BildZeiger)
   FreeImage_Unload(BildZeiger32bit)

Als Letztes nun den Texturnamen(Nummer), den OpenGL für
unser Bild vergeben hat, an die diese Function aufrufende
Zeile zurückgeben:

   BildToOgl=TexturNummer
END FUNCTION

Damit steht diese Function.

Im Hauptprogramm müssen wir jetzt noch den Aufruf
für diese Function ändern, da diese jetzt anderst
heißt (die Zeilen mit den **************im Kommentartext ändern:

glMatrixMode GL_MODELVIEW                      ' Auf den Matrix-Modus Modelview schalten
glLoadIdentity                                 ' und auch diesen auf Anfangswerte setzen
TexturNummer = BildToOgl("mauer_128.bmp")      ' ************************Bitmapdatei als Textur an OpenGL schicken
TexturZwei   =  BildToOgl("Kirschen_256.bmp")  ' ************************eine zweite Bitmapdatei als Textur an OpenGL schicken
glClearColor 0.5, 0.5, 0.50, 0.0               ' Setze Farbe für löschen auf Mittelgrau
glClearDepth 1.0                               ' Depth-Buffer Löschen erlauben
glEnable GL_DEPTH_TEST                         ' den Tiefentest GL_DEPTH_TEST einschalten
glClear GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT  'Tiefen- und Farbpufferbits löschen
'---------------------------
'HAUPTTEIL
'---------------------------

Dabei habe ich jetzt die Bilddateien wie bei der vorigen
Routine beibehalten, also mauer_128.bmp und Kirschen_256.bmp.

Sie können nun auch gerne eine fast x-beliebige andere
Datei zum Texturieren nehmen, ob Jpg oder Png, egal.
NUR DIE GRUNDBEDINGUNGEN VON OPENGL sind einzuhalten,
quadratisches Bild (Breite=Hoehe) und drei verschiedene
Kantenlängen sind möglich: 64, 128 oder 256 Pixel.

Fortgeschrittene können nun auch Bilder mit Alphakanal,
also Teil-Durchsichtig verwenden ;-).

Einziges bis jetzt festgestelltes Manko:
eine mit CorelPaint-7 erstellte Jp2-Datei
nimmt FreeImage nicht an, keine Ahnung, was
da schief läuft.

Hier nochmal das gesamte Listing unserer kleinen 3D-Welt
mit den zwei Wänden und den beiden Pyramiden, jeweils
eins mit Mauer_128 texturiert und das Andere mit Kirschen_256.

'REM Tutorial 4 - OpenGL-Tutorial von Eastler
'-------------------------
'DIMs
'-------------------------
DIM SHARED AS STRING Tastendruck
DIM SHARED AS STRING Tlinks, Trechts, Tvor, Tzurueck, TCtrlVor, TCtrlZurueck, TCtrlLinks, TCtrlRechts
Tlinks       = CHR(255) & CHR( 75)  :' beim Drücken der Taste CursorLinks gibt die Tastatur CHR(255) & CHR( 75) zurück
Trechts      = CHR(255) & CHR( 77)  :' CursorRechts
Tvor         = CHR(255) & CHR( 72)  :' CursorHoch
Tzurueck     = CHR(255) & CHR( 80)  :' CursorRunter
TCtrlVor     = CHR(255) & CHR(141)  :'Ctrl oder STRG zusammen mit CursorHoch
TCtrlZurueck = CHR(255) & CHR(145)  :'Ctrl oder STRG zusammen mit CursorRunter
TCtrlLinks   = CHR(255) & CHR(115)  :'<--------CURSOR NACH LINKS-Variable mit Wert belegen
TCtrlRechts  = CHR(255) & CHR(116)  :'<--------CURSOR NACH RECHTS-Variable mit Wert belegenDIM AS SINGLE XRichtg, YRichtg, ZRichtg
DIM AS SINGLE XRichtg, YRichtg, ZRichtg
DIM AS SINGLE PyraXDrehw, PyraYDrehw, PyraZDrehw :'<------------------------ für Pyramide per Tasten drehen
DIM AS SINGLE WeltDrehX, WeltDrehY, WeltDrehZ
DIM AS INTEGER ConNr                :' Dateinummerspeicher beim Consolefenster öffnen
DIM AS UINTEGER TexturNummer, TexturZwei
DIM SHARED AS DOUBLE Pi
Pi = 3.14159265358979323846
'-------------------------
'Includes
'-------------------------
#include "fbgfx.bi"
#include once "GL/gl.bi"
#include once "GL/glu.bi"
#include once "vbcompat.bi" '************** um Zahlen per "format()" formatieren zu können
#include "FreeImage393.bi"     '************** Bilder aller Art von Festplatte einlesen

'-------------------------
'Declarationen
'-------------------------
DECLARE SUB Koordinatensystem(was AS STRING, TxtPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
DECLARE SUB Schachbrettboden(was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
DECLARE SUB Pyramide (was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
DECLARE FUNCTION BildToOgl(BildDateiName AS STRING) AS INTEGER
DECLARE SUB Schreibe(Text AS STRING)
'-------------------------
' das Fenster öffnen
'-------------------------
screen 19, 32, , 2

'-------------------------
' Open-GL Init
'-------------------------
glViewport 0, 0, 800, 600                      ' den Current Viewport auf eine Ausgangsposition setzen
glMatrixMode GL_PROJECTION                     ' Den Matrix-Modus Projection wählen
glLoadIdentity                                 ' Diesen Modus auf Anfangswerte setzen
gluPerspective 45.0, 800.0/600.0, 0.1, 100.0   ' Grundeinstellungen des Anezeigefensters festlegen
glMatrixMode GL_MODELVIEW                      ' Auf den Matrix-Modus Modelview schalten
glLoadIdentity                                 ' und auch diesen auf Anfangswerte setzen
TexturNummer = BildToOgl("Mauer_128.bmp")       ' Bitmapdatei als Textur an OpenGL schicken
TexturZwei   =  BildToOgl("Kirschen_256.bmp")   ' eine zweite Bitmapdatei als Textur an OpenGL schicken
glClearColor 0.5, 0.5, 0.50, 0.0               ' Setze Farbe für löschen auf Mittelgrau
glClearDepth 1.0                               ' Depth-Buffer Löschen erlauben
glEnable GL_DEPTH_TEST                         ' den Tiefentest GL_DEPTH_TEST einschalten
glClear GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT  'Tiefen- und Farbpufferbits löschen
'---------------------------
'HAUPTTEIL
'---------------------------
Schachbrettboden("SetzKantenLaenge", "", 0.5,  0,    0)
Schachbrettboden("SetzQuadsZahl",    "", 24,   24,   0)     :'6 Einheiten +und- = 12Einheiten Koordiantensystem, bei 0.5 Quadgröße 24 Stück
Schachbrettboden("SetzFarbe1",       "", 0,    0,    0.5)   :'erste Farbe dunkleres blau
Schachbrettboden("SetzFarbe2",       "", 0.25, 0.25, 0.25)  :'erste Farbe dunkles grau
Schachbrettboden("SetzStartPos",     "", -6,   -6,   -1)     :'ganz vorne ganz links beginnen, Boden auf Hoehe(3.Para)-1 verlegen(Y)

Pyramide("SetzLaengen" , "", 1, 1,  1)
Pyramide("SetzFarbe1"  , "", 1, 0, 0)
Pyramide("SetzFarbe2"  , "", 0, 1, 0)
Pyramide("SetzFarbe3"  , "", 0, 0, 1)
Pyramide("SetzFarbe4"  , "", 1, 1, 0)
Pyramide("SetzFarbe5"  , "", 1, 0, 1)

DO UNTIL Tastendruck = CHR(27)                         :'die Schleife solange immer wiederholen, bis in der Variablen Tastendruck die Esc-Taste (chr(27) steht
   Tastendruck = INKEY                                 :'Jeder Tastendruck wird sofort in die Variable Tastendruck gespeichert
   '---------------------------
   'ProgrammSchleife
   '---------------------------

   'JE NACH TASTENDRUCK DEN ENTSPRECHENDEN POSITIONSWERT VERÄNDERN
   SELECT CASE Tastendruck
      CASE "K", "k"
         Koordinatensystem("AnAus", "", 0, 0, 0) :' Schalter umstellen, falls An ist, auf Aus, sonst auf An
      CASE TCtrlrechts                :'Falls die Tasten "Cursor nach rechts" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         XRichtg = XRichtg - COS((WeltDrehY * Pi) / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg - SIN((WeltDrehY * Pi) / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TCtrllinks                 :'Falls die Tasten "Cursor nach rechts" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         XRichtg = XRichtg + COS((WeltDrehY * Pi) / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg + SIN((WeltDrehY * Pi) / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TCtrlzurueck               :'Falls die Tasten "Cursor unten" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         YRichtg = YRichtg + 0.04 :'HOCH und Runter bleibt unberührt von der Y-Drehung
      CASE TCtrlvor                   :'Falls die Tasten "Cursor hoch" zuammen mit Strg- bzw. Ctrl gedrückt wurde
         YRichtg = YRichtg - 0.04 :'HOCH und Runter bleibt unberührt von der Y-Drehung
      CASE TZurueck                   :'Falls die Taste "Cursor runter" gedrückt wurde
         XRichtg = XRichtg + COS((WeltDrehY-90) * Pi / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg + SIN((WeltDrehY-90) * Pi / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TVor                       :'Falls die Taste "Cursor hoch" gedrückt wurde
         XRichtg = XRichtg + COS((WeltDrehY+90) * Pi / 180) * 0.04 :' Den X-Pos-Wert anpassen
         ZRichtg = ZRichtg + SIN((WeltDrehY+90) * Pi / 180) * 0.04 :' Den Y-Pos-Wert anpassen
      CASE TLinks                      :'Falls die Taste "Cursor links" gedrückt wurde
         WeltDrehY = WeltDrehY - 1
      CASE TRechts                     :'Falls die Taste "Cursor rechts" gedrückt wurde
         WeltDrehY = WeltDrehY + 1
      CASE "x"
         PyraXDrehw=PyraXDrehw+12
      CASE "X"
         PyraXDrehw=PyraXDrehw-12
      CASE "y"
         PyraYDrehw=PyraYDrehw+12
      CASE "Y"
         PyraYDrehw=PyraYDrehw-12
      CASE "z"
         PyraZDrehw=PyraZDrehw+12
      CASE "Z"
         PyraZDrehw=PyraZDrehw-12
      CASE "i", "I"                       :'<------------------------
         ConNr = FREEFILE                 :'nächste freie Dateinummer für öffnen von FreeBasic holen und in ConNr merken
         open "con" FOR OUTPUT AS #ConNr  :'und nun die Konsole als Datei unter Nr ConNr öffnen
            PRINT #ConNr, "PyraXDrehw:" & format(PyraXDrehw,"0.00") & "  PyraYDrehw:" & format(PyraYDrehw,"0.00") & "  PyraZDrehw:" & format(PyraZDrehw,"0.00")
            PRINT #ConNr, "XRichtg:" & format(XRichtg,"0.00") & "  YRichtg:" & format(YRichtg,"0.00") & "  ZRichtg:" & format(ZRichtg,"0.00")
         CLOSE #ConNr
   END SELECT   'PRÜFEN, DASS DIE POSITIONSWERTE IN ALLEN DREI RICHTUNGEN ZWISCHEN -3 und +3 BLEIBEN
   IF XRichtg >  6 THEN XRichtg =  6           :'falls zu weit rechts, bei  6 festnageln
   IF XRichtg < -6 THEN XRichtg = -6           :'falls zu weit links,  bei -6 festnageln
   IF YRichtg >  6 THEN YRichtg =  6           :'falls zu weit hoch,   bei  6festnageln
   IF YRichtg < -6 THEN YRichtg = -6           :'falls zu weit runter, bei -6 festnageln
   IF ZRichtg >  5 THEN ZRichtg =  5           :'falls zu weit zurück, bei 10 festnageln
   IF ZRichtg < -10 THEN ZRichtg = -10           :'falls zu weit vor,    bei -6 festnageln
   glClear GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT  :'bisherig erstellte Objekte löschen, unsere 3D-Welt wieder von Neuem an erstellen
   glPushMatrix                                        :'aktuelle Position + Drehgrade sichern (2.Zettel mit gleicher Pos auf PositionsSTACK)
    glRotatef WeltDrehY, 0, 1, 0                       :'Rotationsbefehl der Kamera, um Y ist zu drehen
    glTranslatef XRichtg, YRichtg, ZRichtg             :'Verschiebung der Kamera, auf neue Werte einstellen

    '-------------------------
    'AB HIER OBJEKTE ERSTELLEN
    '-------------------------
    glPointSize(5)                             :' Punktgröße auf 5 = deutlich groß
    glColor3f 1.0,0.0,0.0                      :' Anzeigefarbe auf ROT setzen
    Koordinatensystem("Anzeigen", "", 0, 0, 0) :' den Stringwert "Anzeigen" übergeben
    Schachbrettboden("BeschreibungsListeBoden", "", 0, 0, 0):' SchachbrettBoden in der 3D-Welt erstellen
    glPushMatrix                                                             :'akt.Position und Drehgrade sichern
    glEnable GL_TEXTURE_2D                    :'***********Texturieren erlauben*************
    glBindTexture GL_TEXTURE_2D, TexturNummer :'***********das erste Bild(mauer) als Textur aktivieren*************
    Pyramide("BeschreibungsListe"  , "", PyraXDrehw, PyraYDrehw, PyraZDrehw) :'die erste Pyramide
    glPopMatrix                                                              :'auf gesichterte Position und Drehgrade zurücksetzen
    glPushMatrix                                                             :'akt.Position und Drehgrade sichern
    glTranslatef -2, 0, 0                                                    :'hier der Verschiebebefehl, damit die 2.Pyramide woanderst steht
    glBindTexture GL_TEXTURE_2D, TexturZwei   :'***********das zweite Bild(Kirschen) als Textur aktivieren*************
    Pyramide("BeschreibungsListe"  , "", PyraXDrehw, PyraYDrehw, PyraZDrehw) :'und eine zweite Pyramide
    glPopMatrix                                                              :'auf gesichterte Position und Drehgrade zurücksetzen
    glDisable GL_TEXTURE_2D                    :'***********Texturieren wieder ausschalten*************

    '-----------------
    'OBJEKTE ERSTELLEN
    '-----------------

'-------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXX RECHTECK MIT TEXTUR ERZEUGEN XXXXXXXXXXXXXXXX
'-------------------------------------------------------------------
   glEnable GL_TEXTURE_2D          :'Texturieren mit dem grad aktiven Bild einschalten/erlauben
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL) :'texturieren "voll drauf" kein durchsichtig etc
   glBindTexture GL_TEXTURE_2D, TexturNummer      :'Unser Bild auf der Grafikkarte als aktives setzen
   glColor3f 1.0,0.0,0.0                          :'Anzeigefarbe (für das Rechteck) auf ROT setzen
   glBegin GL_QUADS                               :'Beginn Beschreibungsliste Objekt Viereck
      glTexCoord2d 0.0, 1.0 : glVertex3f -1.0,  1.0, -4.0  :'die linke obere Ecke des Rechtecks
      glTexCoord2d 0.0, 0.0 : glVertex3f -1.0, -1.0, -4.0  :'die linke untere Ecke des Rechtecks
      glTexCoord2d 1.0, 0.0 : glVertex3f +1.0, -1.0, -4.0  :'die rechte untere Ecke des Rechtecks
      glTexCoord2d 1.0, 1.0 : glVertex3f +1.0,  1.0, -4.0  :'die rechte obere Ecke des Rechtecks
   glEnd                                          :'Fertig mit dem Objekt
   glDisable GL_TEXTURE_2D         :'Texturieren ausschalten (es soll ja nur das Viereck texturiert werden, nicht die Pyramiden)
'-------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
'-------------------------------------------------------------------
   glEnable GL_TEXTURE_2D          :'Texturieren mit dem grad aktiven Bild einschalten/erlauben
   glEnable GL_Alpha_test          :'Texturieren mit dem grad aktiven Bild einschalten/erlauben
   glBindTexture GL_TEXTURE_2D, TexturZwei        :'Unser Bild auf der Grafikkarte als aktives setzen
   glColor3f 1.0,0.0,0.0                          :'Anzeigefarbe (für das Rechteck) auf ROT setzen
   glBegin GL_QUADS                               :'Beginn Beschreibungsliste Objekt Viereck
      glTexCoord2d 0.0, 1.0 : glVertex3f -4.0,  1.0, -4.0  :'die linke obere Ecke des Rechtecks
      glTexCoord2d 0.0, 0.0 : glVertex3f -4.0, -1.0, -4.0  :'die linke untere Ecke des Rechtecks
      glTexCoord2d 1.0, 0.0 : glVertex3f -2.0, -1.0, -4.0  :'die rechte untere Ecke des Rechtecks
      glTexCoord2d 1.0, 1.0 : glVertex3f -2.0,  1.0, -4.0  :'die rechte obere Ecke des Rechtecks
   glEnd                                          :'Fertig mit dem Objekt
   glDisable GL_TEXTURE_2D         :'Texturieren ausschalten (es soll ja nur das Viereck texturiert werden, nicht die Pyramiden)
'-------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
'-------------------------------------------------------------------


    flip                                               :'liebes OpenGL, zeig alles, was in der Schleife für dich vornedran steht, auf Monitor an
   '---------------------------
   'Ende der Schleife
   '---------------------------
   glPopMatrix                                         :' Aufgabe erledigt, den zweiten Zettel mit der geänderten Pos wegschmeißen, dann ist Alte Pos wieder aktuelle Pos
LOOP

END

'--------------------------------
SUB Koordinatensystem(was AS STRING, TxtPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
   DIM AS INTEGER Zaehler
   STATIC AS INTEGER AnzeigeSchalter
   SELECT CASE UCASE(was)
      CASE "ANAUS"
         IF AnzeigeSchalter = 0 THEN AnzeigeSchalter = 1 ELSE AnzeigeSchalter = 0
      CASE "ANZEIGEN"
        IF AnzeigeSchalter = 1 THEN
         glBegin GL_LINES:' Ja, wir beginnen mit der Beschreibungsliste fuer Linien
         'und zwar zuerst die positiven Bereiche jeder Achse
         glColor3f 1.0, 1.0, 1.0        :' Positives Maß = weiße Teilstriche (Rot 1.0, blau 1.0, grün 1.0 = weiß)
         FOR Zaehler = 1 TO 6 :'fuer die Maßeinheitspositionen 1, 2 und 3 Striche Ziehen
            'X-ACHSE
               'senkrechter Strich (wie Y-Achse)
            glVertex3f  Zaehler+Para1,  0.2+Para2,  0.0+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1, -0.2+Para2,  0.0+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
               'waagerechter Strich (wie Z-Achse)
            glVertex3f  Zaehler+Para1,  0.0+Para2,  0.2+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1,  0.0+Para2, -0.2+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
            'Y-ACHSE
               'Strich wie X-Achse
            glVertex3f  0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach +X, Tiefe = 0
            glVertex3f -0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach -X, Tiefe = 0
               'Strich wie Z-Achse
            glVertex3f  0.0+Para1, Zaehler+Para2,  0.2+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach vorne/+Z, Seite (X) = 0
            glVertex3f  0.0+Para1, Zaehler+Para2, -0.2+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach hinten/-Z, Seite (X) = 0
            'Z-ACHSE
               'Strich waagerecht (X)
            glVertex3f  0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach rechts +X, Hoehe = 0
            glVertex3f -0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach links -X, Hoehe = 0
               'Strich senkreicht (Y)
            glVertex3f  0.0+Para1,  0.2+Para2,  Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach oben +Y, Seite = 0
            glVertex3f  0.0+Para1, -0.2+Para2,  Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach unten -Y, Seite = 0
         NEXT Zaehler
         glColor3f 0.0, 0.0, 0.0        :' Negatives Maß = schwrze Teilstriche (alles auf 0)
         FOR Zaehler = -6 TO -1 :'fuer die Maßeinheitspositionen -3, -2 und -1 Striche Ziehen
         'X-ACHSE
            'senkrechter Strich (wie Y-Achse)
            glVertex3f  Zaehler+Para1,  0.2+Para2,  0.0+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1, -0.2+Para2,  0.0+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
            'waagerechter Strich (wie Z-Achse)
            glVertex3f  Zaehler+Para1,  0.0+Para2,  0.2+Para3 :' Anfangspunkt auf XAchse-Maßpunkt, 0.2 Einheiten drüber(+Y), Tiefe = 0
            glVertex3f  Zaehler+Para1,  0.0+Para2, -0.2+Para3 :' Endpunkt auf XAchse-Maßpunkt, 0.2 Einheiten drunter(-Y), Tiefe = 0
            'Y-ACHSE
               'Strich wie X-Achse
            glVertex3f  0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach +X, Tiefe = 0
            glVertex3f -0.2+Para1, Zaehler+Para2,  0.0+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach -X, Tiefe = 0
               'Strich wie Z-Achse
            glVertex3f  0.0+Para1, Zaehler+Para2,  0.2+Para3 :' Anfangspunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach vorne/+Z, Seite (X) = 0
            glVertex3f  0.0+Para1, Zaehler+Para2, -0.2+Para3 :' Endpunkt auf YAchse-Maßpunkt, 0.2 Einheiten nach hinten/-Z, Seite (X) = 0
            'Z-ACHSE
               'Strich waagerecht (X)
            glVertex3f  0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach rechts +X, Hoehe = 0
            glVertex3f -0.2+Para1,  0.0+Para2, Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach links -X, Hoehe = 0
               'Strich senkreicht (Y)
            glVertex3f  0.0+Para1,  0.2+Para2,  Zaehler+Para3 :' Anfangspunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach oben +Y, Seite = 0
            glVertex3f  0.0+Para1, -0.2+Para2,  Zaehler+Para3 :' Endpunkt auf ZAchse-Maßpunkt, 0.2 Einheiten nach unten -Y, Seite = 0
         NEXT Zaehler
         'UND NUN DIE DREI ACHSEN SELBST:
         'X-ACHSE
         glColor3f 1.0, 0.0, 0.0        :' Xachse = rot
         glVertex3f  -6.0+Para1, 0+Para2, 0+Para3
         glVertex3f  +6.0+Para1, 0+Para2, 0+Para3
         'Y-ACHSE
         glColor3f 0.0, 1.0, 0.0        :' Yachse = grün
         glVertex3f  0+Para1, -6.0+Para2, 0+Para3
         glVertex3f  0+Para1, +6.0+Para2, 0+Para3
         'Z-ACHSE
         glColor3f 0.0, 0.0, 1.0        :' Zachse = blau
         glVertex3f  0+Para1, 0+Para2, -6.0+Para3
         glVertex3f  0+Para1, 0+Para2, +6.0+Para3
         glEnd
       END IF
   END SELECT
END SUB
'-------------------------
SUB Schachbrettboden(was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
   STATIC AS SINGLE QuadKantenLaenge, QuadsQuer, QuadsTief
   STATIC AS SINGLE Farbe1Rot, Farbe1Gruen, Farbe1Blau
   STATIC AS SINGLE Farbe2Rot, Farbe2Gruen, Farbe2Blau
   STATIC AS SINGLE StartPosX, StartPosZ, BodenHoehe
   STATIC AS INTEGER ZaehlerX, ZaehlerZ                   :'ForNext-Zählvars, ggf. rekursiver aufruf dieser Sub, drum STATIC
   SELECT CASE UCASE(was)
      CASE "SETZKANTENLAENGE"
         'Aufruf hierfür in Para1 die Länge der Kanten der Quadrate
         QuadKantenLaenge = Para1
      CASE "SETZQUADSZAHL"
         'Aufruf hierfür in Para1 die Anzahl von Quads quer,
         'in Para2 die Anzahl in der Tiefenrichtung
         QuadsQuer = Para1
         QuadsTief = Para2
      CASE "SETZFARBE1"
         'in Para1 Para2 Para2 die Rot Grün und Blauwerte der ersten Quadratfarbe
         Farbe1Rot   = Para1
         Farbe1Gruen = Para2
         Farbe1Blau  = Para3
      CASE "SETZFARBE2"
         'in Para1 Para2 Para2 die Rot Grün und Blauwerte der ersten Quadratfarbe
         Farbe2Rot   = Para1
         Farbe2Gruen = Para2
         Farbe2Blau  = Para3
      CASE "SETZSTARTPOS"
         'in Para1 Para2 Para2 die X und Z Position, von wo begonnen wird,
         'die vielen Quadrate zu erstellen
         StartPosX   = Para1
         StartPosZ   = Para2
         BodenHoehe  = Para3
      CASE "BESCHREIBUNGSLISTEBODEN"
         'Hier erstellen wir die FOR-NEXT-SCHLEIFEN,
         'welche die OpenGL-Beschreibungsliste
         'zum Anzeigen des Bodens erstellen
         glBegin GL_QUADS
            FOR ZaehlerX = 1 TO QuadsQuer     :'Laufnr des grad zu zeichnenden Quads auf der X-Achse
               FOR ZaehlerZ = 1 TO QuadsTief  :'Laufnr des grad zu zeichnenden Quads auf der Z-Achse
                  'Die Farbe festlegen
                  IF ((ZaehlerX+ZaehlerZ)\2)*2=(ZaehlerX+ZaehlerZ) THEN
                     'Wenn die Summe von ZahlerX+ZaehlerY gerade ist Farbe1 nehmen
                     glColor3f Farbe1Rot, Farbe1Gruen, Farbe1Blau
                  ELSE
                     'Wenn die Summe von ZahlerX+ZaehlerY UNgerade ist Farbe2 nehmen
                     glColor3f Farbe2Rot, Farbe2Gruen, Farbe2Blau
                  END IF
                  'Die Eckpunkte der Quadrate benennen wir in der Reihenfolge GEGEN DEN UHRZEIGERSINN, von oben draufschauend
                  glVertex3f StartPosX+(ZaehlerX-1)*QuadKantenLaenge,  BodenHoehe,  StartPosZ+(ZaehlerZ-1)*QuadKantenLaenge
                  glVertex3f StartPosX+ ZaehlerX   *QuadKantenLaenge,  BodenHoehe,  StartPosZ+(ZaehlerZ-1)*QuadKantenLaenge
                  glVertex3f StartPosX+ ZaehlerX   *QuadKantenLaenge,  BodenHoehe,  StartPosZ+ ZaehlerZ   *QuadKantenLaenge
                  glVertex3f StartPosX+(ZaehlerX-1)*QuadKantenLaenge,  BodenHoehe,  StartPosZ+ ZaehlerZ   *QuadKantenLaenge
               NEXT ZaehlerZ
            NEXT ZaehlerX
         glEnd
      CASE ELSE
         'Hier kommen alle SUB-Aufrufe an, welche als
         'was-Parameter einen Eintrag haben, der hier
         'nicht ausgewertet wurde.
         'Tippfehler vom Programmierer????
   END SELECT
END SUB
'-------------------------
SUB Pyramide(was AS STRING, StrPara AS STRING, Para1 AS SINGLE, Para2 AS SINGLE, Para3 AS SINGLE)
   'Pyramide erstellen, Grundfläche/Quadrat = auf Höhe 0, seitlich mittig auf X- und Z-Achse Positioniert
   'Grundflächengroesse = XLaenge x ZLaenge, Höhe Pyramide = ZLaenge
   STATIC AS SINGLE XLaenge, YLaenge, ZLaenge
   STATIC AS SINGLE Farbe1Rot, Farbe1Gruen, Farbe1Blau
   STATIC AS SINGLE Farbe2Rot, Farbe2Gruen, Farbe2Blau
   STATIC AS SINGLE Farbe3Rot, Farbe3Gruen, Farbe3Blau
   STATIC AS SINGLE Farbe4Rot, Farbe4Gruen, Farbe4Blau
   STATIC AS SINGLE Farbe5Rot, Farbe5Gruen, Farbe5Blau
   STATIC AS INTEGER ZaehlerX, ZaehlerZ                   :'ForNext-Zählvars, ggf. rekursiver aufruf dieser Sub, drum STATIC
   SELECT CASE UCASE(was)
      CASE "SETZLAENGEN"  : XLaenge   = Para1 : YLaenge     = Para2 : ZLaenge     = Para3
      CASE "SETZFARBE1"   : Farbe1Rot = Para1 : Farbe1Gruen = Para2 : Farbe1Blau  = Para3 :'ein Dreieck
      CASE "SETZFARBE2"   : Farbe2Rot = Para1 : Farbe2Gruen = Para2 : Farbe2Blau  = Para3 :'ein Dreieck
      CASE "SETZFARBE3"   : Farbe3Rot = Para1 : Farbe3Gruen = Para2 : Farbe3Blau  = Para3 :'ein Dreieck
      CASE "SETZFARBE4"   : Farbe4Rot = Para1 : Farbe4Gruen = Para2 : Farbe4Blau  = Para3 :'ein Dreieck
      CASE "SETZFARBE5"   : Farbe5Rot = Para1 : Farbe5Gruen = Para2 : Farbe5Blau  = Para3 :'der Boden/Quadrat
      CASE "BESCHREIBUNGSLISTE"
         glrotatef Para1, 1, 0, 0    :'<----------- um X-Achse drehen
         glrotatef Para2, 0, 1, 0    :'<----------- um Y-Achse drehen
         glrotatef Para3, 0, 0, 1    :'<----------- um Z-Achse drehen
         'die OpenGL-Beschreibungsliste zum Anzeigen der Pyramide erstellen
         glBegin GL_QUADS
            'der Boden der Pyramide als Quadrat, auf Höhe 0(Y-Achse),
            'seitliche Ausrichtungen = Quadratmitte = X-Achsenmitte und Z-Achsenmitte
            'damit Zentriert sitzt, für 1.Punkt einfach halbe Kantenlänge von 0 Abziehen, für 2. dazuaddieren
            'Reihenfolge Eckpunktangabe gegen Uhrzeigersinn VON UNTEN her gesehen (unten=Außenseite später)
            glColor3f  Farbe5Rot, Farbe5Gruen, Farbe5Blau :'falls Texturieren Disabled ist, wird diese Farbe genommen
            glTexCoord2d 0.0, 1.0 : glVertex3f 0-(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'Ecke links oben
            glTexCoord2d 0.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'Ecke links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'Ecke rechts unten
            glTexCoord2d 1.0, 1.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'Ecke rechts oben
         glEnd
         glBegin GL_TRIANGLES
            '   Dreieckseite 1 = linke Seite
            glColor3f  Farbe1Rot, Farbe1Gruen, Farbe1Blau
            glTexCoord2d 0.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0       , 0-(ZLaenge/2) :'links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0       , 0+(ZLaenge/2) :'rechts unten
            glTexCoord2d 0.1, 1.0 : glVertex3f 0                , YLaenge , 0             :'Spitze oben
            '   Dreieckseite 2 = hintere Seite /Rückseite
            glColor3f  Farbe2Rot, Farbe2Gruen, Farbe2Blau
            glTexCoord2d 0.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'rechts unten
            glTexCoord2d 0.5, 1.0 : glVertex3f 0                , YLaenge , 0        :'Spitze oben mitte
            '   Dreieckseite 3 = rechte Seite
            glColor3f  Farbe3Rot, Farbe3Gruen, Farbe3Blau
            glTexCoord2d 0.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0-(ZLaenge/2) :'rechts unten
            glTexCoord2d 0.5, 1.0 : glVertex3f 0                , YLaenge , 0        :'Spitze oben mitte
            '   Dreieckseite 4 = Vorderseite
            glColor3f  Farbe4Rot, Farbe4Gruen, Farbe4Blau
            glTexCoord2d 0.0, 0.0 : glVertex3f 0-(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'links unten
            glTexCoord2d 1.0, 0.0 : glVertex3f 0+(XLaenge/2)    , 0  , 0+(ZLaenge/2) :'rechts unten
            glTexCoord2d 0.5, 1.0 : glVertex3f 0                , YLaenge , 0        :'Spitze oben mitte
         glEnd
      CASE ELSE
         'Hier kommen alle SUB-Aufrufe an, welche als
         'was-Parameter einen Eintrag haben, der hier
         'nicht ausgewertet wurde.
         'Tippfehler vom Programmierer????
   END SELECT
END SUB
'--------------------------------
FUNCTION BildToOgl(BildDateiName AS STRING) AS INTEGER
   '"Lade Bilddatei von Festplatte in FreeImageRam"
   DIM AS UINTEGER TexturNummer, Breite, Hoehe, ZeilenZaehler, PunktZaehler, BlauWert, RotWert, DateiNummer
   DIM AS FIBITMAP Ptr BildZeiger
   DIM AS ZSTRING PTR DateiNamenZeiger
   DIM AS FREE_IMAGE_FORMAT Ergebnis
   DIM DateiName AS ZSTRING * 512
   DateiName=LEFT(BildDateiName, 512)
   'mit FreeBasic checken, ob die Datei überhaupt existiert
   DateiNummer = FREEFILE
   OPEN DateiName FOR INPUT AS #DateiNummer
   IF LOF(DateiNummer) = 0 THEN
      SCHREIBE("DateiName Läenge=" & LEN(DateiName) & "=" & LEFT(DateiName, 20) & " Dateigroeße=" & LOF(DateiNummer) )
      END
   END IF
   CLOSE #DateiNummer
   Ergebnis = FIF_UNKNOWN :' Erst mal "Dateityp Unbekannt" reinsetzen, FIF_UNKNOWN = -1
   'Dateityp ermitteln:
   Ergebnis = FreeImage_GetFileType( @DateiName, 0)
   IF Ergebnis = FIF_UNKNOWN THEN
      SCHREIBE("erster Test mit " & DateiName & " schlug fehl, nehme Zweiten!")
      'mit der schnellen Routine kann das Dateiformat nicht ermittelt werden,
      'über die tiefer gehende Routine den Typ ermitteln
      Ergebnis = FreeImage_GetFIFFromFilename( @DateiName )
      IF Ergebnis <> FIF_UNKNOWN AND FreeImage_FIFSupportsReading(Ergebnis) THEN
         BildZeiger = FreeImage_Load( Ergebnis, @DateiName, 0 )
      ELSE
         'Auch die tiefergehende Routine schlägt fehl, Bild einlesen geht nicht!
         Schreibe("kann die Datei " & DateiName & " nicht einlesen, breche ab")
         END
      END IF
   ELSE
      BildZeiger = FreeImage_Load( Ergebnis, @DateiName, 0 )
   END IF
   'Nun steht das Bild im FreeImageFormat im Ram an Adresse BildZeiger

   'eine Kopie davon mit 32-bit Farbtiefe erstellen
   DIM AS FIBITMAP Ptr BildZeiger32bit
   BildZeiger32bit = FreeImage_ConvertTo32Bits( BildZeiger )
   'Pointer/Zeiger auf das Erste Pixel im Ram holen
   DIM AS BYTE PTR PixelImRam, RamPixelKopie
   PixelImRam = FreeImage_GetBits( BildZeiger32bit )
   RamPixelKopie = PixelImRam
   'Bildbreite/Hoehe in Pixel holen
    Breite = FreeImage_GetWidth(BildZeiger32bit)
    Hoehe = FreeImage_GetHeight(BildZeiger32bit)
   IF (Breite <> Hoehe) OR (Breite <> 64 AND Breite <> 128 AND Breite <> 256) THEN
      'Das Bild kann nicht als Textur für OpenGL 1.0 verwendet werden
      SCHREIBE("Das Bild " & DateiName & "kann nicht als Textur für OpenGL 1.0 verwendet werden")
      SCHREIBE("           Das Bild ist entweder nicht quadratisch, oder ")
      SCHREIBE("           die Seitenlängen sind nicht 64, 128 oder 256 Pixel!")
      SCHREIBE("           Breche ab!")
      'Ramspeicher von FreeImage freigeben
      FreeImage_Unload(BildZeiger)
      FreeImage_Unload(BildZeiger32bit)
      END
   END IF
   'ROT UND BLAU TAUSCHEN
   FOR ZeilenZaehler = 0 TO Hoehe-1
      FOR PunktZaehler = 0 TO Breite-1
         BlauWert = RamPixelKopie[0]        :'aus Ram das Byte mit dem Blauwert des grad zu bearbeitenden Pixels holen
         RotWert  = RamPixelKopie[2]        :'aus Ram das Byte mit dem Rotwert des grad zu bearbeitenden Pixels holen
         RamPixelKopie[0] = RotWert         :'den Rotwert an die Stelle des Blauwertes im RAM schreiben
         RamPixelKopie[2] = BlauWert        :'den Blauwert an die Stelle des Rotwertes im RAM schreiben
         RamPixelKopie = RamPixelKopie+4 :'Ram-Adresse um 4 bytes erhöhen = Start Pixeldaten des nächsten Pixels
      NEXT PunktZaehler
   NEXT ZeilenZaehler
   'Farbtiefe des OpenGL-Fensters holen
   DIM AS INTEGER FensterFarbTiefe
   SCREENCONTROL 5, FensterFarbTiefe :' 5 = GET_SCREEN_DEPTH : Farbtiefe in Bits des OpenGL-Fensters ermitteln
   'XXXXXXXXXXXXXXXXXXXXX UND RAM-BILD AN OPENGL ÜBERGEBRN XXXXXXXXXXXXXXXX
   glGenTextures 1, @TexturNummer
   glBindTexture GL_TEXTURE_2D, TexturNummer
   IF FensterFarbTiefe = 32 OR FensterFarbTiefe = 24 THEN
      glTexImage2D GL_TEXTURE_2D, 0, GL_RGBA, Breite, Hoehe, 0, GL_RGBA, GL_UNSIGNED_BYTE, PixelImRam
   ELSE
      glTexImage2D GL_TEXTURE_2D, 0, GL_RGB, Breite, Hoehe, 0, GL_RGBA, GL_UNSIGNED_BYTE, PixelImRam
   END IF
   glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR
   glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR
   'die von FreeImage belegten Bildspeicher wieder freigeben
   FreeImage_Unload(BildZeiger)
   FreeImage_Unload(BildZeiger32bit)
   'als Funktionsrückgabe den Namen/Nummer der OpenGL-Textur setzen
   BildToOgl=TexturNummer
END FUNCTION
'-------------------
SUB Schreibe(Text AS STRING)
   DIM AS INTEGER DateiNummer
   'AUF DEN MONITOR IN DIE TEXTCONSOLE
   DateiNummer=FREEFILE
   OPEN "CONS" FOR OUTPUT AS #DateiNummer
   PRINT #DateiNummer, Text
   CLOSE #DateiNummer
   'NOCHMAL IN EINE DATEI
   DateiNummer=FREEFILE
   OPEN "Protokoll.txt" FOR APPEND AS #DateiNummer
   PRINT #DateiNummer, DATE & " " & TIME & " " & Text
   CLOSE #DateiNummer
END SUB

[seitenwechsel]

Schlußwort

so, das waren jetzt knapp 3000 Zeilen Text und Quellcode, nur um
das Thema Texturieren ein bißchen anzureißen.

Ja, Sie lesen richtig, das ist noch längst nicht alles, was mit
Texturen möglich ist.
Z.B. teilweise durchsichtige Texturen sind ja kurz angesprochen
worden, ich hab aber auch schon Texturen gesehen, welche nicht
als Bild auf der Oberfläche erscheinen, sondern der Oberfläche
ein "Relief" eindrücken! Das hat toll ausgesehen!
Oder Texturen, die das belegte Objekt an den Hellen Stellen des
Texturbildes "zum Leuchten" bringen!

Jedoch denke ich, 3000 Zeilen sind erst mal genug, das wichtigste,
Oberflächen mit einer Textur zu bekleben, dürfte jetzt klar sein.

Die anderen Funktionen packe ich irgendwann in ein
TEXTUREN-TEIL2 - Tutorial.

Im nächsten Teil werde ich versuchen, das Anzeigen von Schriften
zu erläutern, bzw. eine von vielen Möglichkeiten dazu.
Hoffe, ich krieg das alles so zusammen, wie ich mir das vorstelle.

bis dahin

euer Eastler


Letzte Bearbeitung: 28.09.2008