http://129.69.35.12/vrml/programming/vrml_programming.html
VRML programmieren
- benötigt den Text des offiziellen
ISO 14772 Standard
-
erfordert, dass man Konzepte wie Modularisierung und Datentypen
verstanden hat
-
kann man in "javascript", weil es bequem ist
-
geht auch in java, wenn man höhere Anforderungen hat
-
erfordert dass man zum Texteditor greifen muss,
wobei "Cosmoworlds" ganz hilfreich sein kann
Inhalt:
Betrachtet man einen VRML Knoten (hier der häufig benutzte "Transform"
Knoten, der z.B. für Verschiebung, Drehung usw. zuständig ist) im
offizellen VRML97 Standard ISO/IEC 14772 (in der Liste der VRML Knoten unter
http://www.web3d.org/technicalinfo/specifications/vrml97/part1/nodesRef.html#Transform
)

kann man 5 Spalten unterscheiden:
-
Die Eventinformation ist wichtig um entscheiden zu
können, wie
verschiedene VRML Knoten über Events (Nachrichten) miteinander
verbunden werden können.
-
Die Information über den Datentyp, die verhindert,
dass Nachrichten ausgetauscht werden können, die nicht zueinander passen.
-
Den Fieldname, der zur eindeutigen Kennzeichnung dient.
-
Den Defaultwert, der benutzt wird, wenn der VRML Knoten
benutzt wird,
ohne dass diesem Fieldname expliziet ein Wert zugewiesen wird
(eventIn und eventOut Fieldnamen haben keinen Defaultwert).
-
Der Wertebereich, der zeigt, welche Werte für diesen Fieldname
zulässig sind.
Darunter befindet sich eine genaue Beschreibung der Bedeutung
der einzelnen Fieldnamen.
Vergleicht man die Notation im Standard mit dem Bild, dass sich in Dune


oder dem Szenengraph in Cosmoworlds

bietet, erkennt man die graphische Darstellung dieser Eventinformation.
Die nach links weisenden Kästchen in Dune (wie z.B. ("set_translation")
deuten darauf hin, dass an den entsprechenden Fieldname eine Nachricht
geschickt werden kann, die nach rechts weisenden Kästchen deuten
darauf hin, dass vom entsprechenden Fieldname eine Nachricht abgeschickt
werden kann. Dabei dienen die Vor/Nachsilben "set_" bzw. "_changed" nur
zur Verdeutlichung, sie können auch weggelassen werden.
Genauso deuten die nach links oder rechts deutenden Pfeilchen im
Cosmoworlds-Szenengraph auf eingehende oder ausgehende Nachrichten hin.
Es lassen sich dabei 4 verschiedene Typen unterscheiden:
-
eventIn: dieser Fieldname kann eine Nachricht empfangen
-
eventOut: dieser Fieldname kann eine Nachricht verschicken
-
exposedField: dieser Fieldname kann sowohl eine Nachricht
empfangen als auch verschicken
-
field: dieser Fieldname kann weder Nachrichten empfangen
noch verschicken.
Das deutet häufig darauf hin, dass die Berechnung
der entsprechenden Daten aufwendig ist. Da sich der Wert während
des Programmlaufs nicht ändern kann, muss diese Berechnung nur einmal
beim Programmstart durchgeführt werden.
Datentypen sind ein wichtiges Programmierprinzip. Sie verhindern,
dass Daten ausgetauscht werden können, die nicht zueinander passen.
Es macht zum Beispiel in der Regel wenig Sinn, wenn eine Zeitinformation
(ein Wert) einer Verschiebung (drei Werte) zugewiesen werden soll.
Über dieses einfache Beispiel hinaus gibt es noch feinere
Unterscheidungen.
Betrachtet man die Liste aller möglichen Datentypen in VRML,
wie in
http://www.web3d.org/technicalinfo/specifications/vrml97/part1/fieldsRef.html#Introduction
so kann man zuerst "SF" und "MF" Typen unterscheiden. Dabei bedeutet
"MF" ("M" von "multiple") eine Liste ("Array") des entsprechenden "SF" Typs.
Diese Liste wird von [ und ]
eingeschlossen (ausser
der Array enthält nur einen Wert).
Einige wichtige Datentypen sind zum Beispiel:
-
SFVec3f: Dieser Datentyp ist für drei zusammengehörende
Werte zuständig.
Beispiel:
translation von Transform representiert eine Verschiebung.
-
SFRotation: Dieser Datentyp ist für Rotationen (vier
Werte: Drehachse und Winkel) zuständig.
Beispiel:
rotation von Transform representiert eine Drehung
-
MFNode: Dieser relativ häufig vorkommende Datentyp beschreibt
eine Aufzählung von VRML-Knoten.
Beispiel:
children von Transform representiert die VRML Knoten, die durch einen
Transformknoten bewegt werden.
-
SFBool: Dieser Datentyp kann nur einen von zwei
verschiedene Werte enthalten: TRUE (wahr) oder FALSE (falsch).
Beispiel:
isOver vom Touchsensor verschickt die Information, ob sich die
Maus (bzw. "Zeige-Gerät") über dem Touchsensor befindet, oder nicht.
-
SFInt32: Dieser Datentyp kann nur einen ganzzahligen Wert
enthalten (zum Beispiel -5, 0, 1, 2).
Beispiel:
whichChoice vom Switchknoten. Er schaltet zwischen verschiedenen
VRML Knoten (deren Liste in choice angegeben werden) um. Dabei wird wie in
VRML/Java üblich mit der Zählung mit 0 begonnen. Negative Werte
bedeuten hier, dass kein VRML Knoten aus der Liste gezeigt wird.
-
SFFloat: Dieser Datentype kann eine beliebige Zahl enthalten
(zum Beispiel -3.141, 0.5, 1.0)
-
MFString: Dieser Datentype enthält eine Aufzählung
von einzelnen Textbausteinen.
Die einzelen Textbausteine werden dabei von " und
" eingeschlossen (z.B. "das ist ein String")
Beispiel:
url von Inline enthält eine Reihe von WWW-Adressen, die nacheinander
durchprobiert werden, solange bis eine gültige Adresse gefunden wird.
Modularisierung ist wahrscheinlich das wichtigste Programmierkonzept
überhaupt. Man versteht darunter die Kunst, ein Problem in isolierte
Teilprobleme zu unterteilen und dabei Übersichtlichkeit, Wartbarkeit
und Wiederverwendbarkeit zu fördern.
Ein einfaches Beispiel:
Einige 3D Modelle von Häusern in VRML sollen daraufhin untersucht werden,
ob eine Drehtür, eine Schiebetür oder eine konventionelle Tür
geeignet sind.
Statt nun jedem 3D Modell (zum Beispiel mit Dune) verschiedene Türen
hinzuzufügen, macht es Sinn, die Türen jeweils einzeln
herzustellen und sie dann mit nur einem Befehl in die 3D Modelle
einzufügen. Das ist übersichtlicher.
Stellt sich Jahre später das Problem, dass in einem anderen 3D Modell
eine solche Tür benötigt wird, ist das sehr einfach möglich.
Das ist ein Beispiel für Wiederverwendbarkeit.
Für Modularisierung sind im VRML-Standard zwei verschiedene Methoden
vorgesehen:
-
Inline
Ist die einfache Möglichkeit, ein anderes VRML-File mit seiner URL
(URL=Universal Resource Locator: "webadresse" (z.B.
"http://129.69.35.12/vrml/scenegraph.wrl"),
kann aber auch ein Filenamen auf dem lokalen Rechner sein)
(beschrieben üder den Fieldname url) an einer bestimmten
Stelle in den Szenengraph einzubinden.
Das Programm Cosmoworlds besitzt über File -> Import as Inline
eine direkte Unterstützung fuer den Inline Knoten.
Die Unterstützung fuer den Inline Knoten im Programm Dune ist
dagegen minimal.
-
PROTO bzw. EXTERNPROTO
Der PROTO Befehl erlaubt es, sich eigene neue VRML Befehle aus den bestehenden
Befehlen zu basteln und dabei alle Featueres wie Eventinformation, Datentypen,
interne ROUTE's usw. zu verwenden.
Über den EXTERNPROTO Befehl lassen sich auch PROTO's verwenden, die
über eine URL erreichbar sind, so dass sich damit leicht
Programmbibleotheken im Internet aufbauen lassen.
Leider hat keiner der bisher verwendeten Programme eine brauchbare
Unterstützung zum Erstellen von PROTO's, man ist also gezwungen,
dafür zum Texteditor zu greifen.
Sinnvollerweise geht man dabei so vor, dass man ein von anderen
Werkzeugen erzeugtes VRML File um die nötigen PROTO Befehle ergäntzt.
Benutzen Sie niemals, wenn ein VRML File PROTO's enthält oder
benutzt, die veraltete Version 0.13/0.14 von Dune.
Dune enthät einen Fehler, der dazu führt, dass das
VRML File bei File -> Save beschädigt wird.
white_dune ab Version 0.17
beschädigt zwar nichts, ist aber leider nicht in der Lage,
Protos oder Inline Knoten korrekt anzuzeigen...
Die Benutzung eines PROTO's ist dagegen in Cosmoworlds möglich.
Ein kleines Beispiel zeigt dies:
Schneiden Sie dazu das folgende PROTO aus
#VRML V2.0 utf8
PROTO Schnitzel
[
exposedField SFColor Sauerkraut 1 0 1
eventOut SFVec3f Wackelpudding
]
{
DEF O_Saft Transform
{
children
[
DEF Kartoffelpuffer PlaneSensor
{
translation_changed IS Wackelpudding
}
Shape
{
appearance Appearance
{
material Material
{
diffuseColor IS Sauerkraut
transparency 0.5
}
}
geometry Sphere { radius 1 }
}
]
}
ROUTE Kartoffelpuffer.translation_changed TO O_Saft.set_translation
}
schreiben Sie es in das File "mittagessen.wrl". Beachten Sie dabei,
dass die Kommentarzeile "#VRML V2.0 utf8" die erste Zeile in einem VRML File
sein muss und dass kein Leerzeichen oder ähnliches diesem
Text vorangestellt werden darf.
Zur Erklärung (nur wenn Sie sich für die Details interessieren):
Dieses Proto "Schnitzel" enthält einen Körper (Shape)
der Geometrie Kugel (Sphere), deren Farbe (diffuseColor) über
den Fieldname
"Sauerkraut" des neuen VRML Befehls "Schnitzel" gesetzt/verschickt werden
kann. Ausserdem enthält "Schnitzel" einen PlaneSensor, so dass
die Verschiebung der Maus auf der Kugel über den Fieldname
"Wackelpudding" als Nachricht verschickt werden kann. Der abschliessende
ROUTE Befehl sorgt dafür, dass die Kugel der Mausbewegung
folgt.
Schneiden Sie sich genauso das
Beispiel für die Benutzung dieses PROTO aus
#VRML V2.0 utf8
EXTERNPROTO Schnitzel
[
exposedField SFColor Sauerkraut
eventOut SFVec3f Wackelpudding
]
"mittagessen.wrl"
Schnitzel
{
Sauerkraut 1 0 0
}
speichern Sie es und laden Sie sich das File mit File -> Import
in Cosmoworlds.

Fügen Sie ein (kleines) Objekt (z.B. Kegel) ein.
Klicken Sie auf das Icon mit dem Scenengraph ("Open Outline Editor")
und öffnen Sie die Gruppierungen (zum Teil mehrmals klicken), bis
sich folgendes Bild ergibt:

Ändern Sie das Field "Sauerkraut" von 1 0 0
(RGB-(Rot/Gruen/Blau)-Farbwerte, also rot) auf 0 0 1 (blau).
Klicken Sie das EventOut Pfeilchen von "Wackelpudding" und auf das
EventIn Pfeilchen von "translation" des benachbarten Transforms,
um sie mit einer ROUTE zu verbinden.

Speichern Sie jetzt das Ergebnis und schauen Sie es sich in netscape an.
Beachten Sie, dass File -> Preview, das Files in einem
temporärerem Verzeichnis ablegt, jetzt nicht funktioniert,
weil sonst das File "mittagessen.wrl" nicht mehr gefunden werden kann.

Obwohl hoffentlich alles geklappt hat (ansonsten sollten Sie einen Blick
in das nächste Kapitel "Fehlersuche" werfen), handelt es sich bei
"mittagessen.wrl" um ein abgrundtief schlechtes Programm, ein Beispiel,
wie man es besser nicht machen sollte.
Im besten Fall lässt sich
darüber sagen, dass offenbar die Sinne des Programmierers vom
Hunger verwirrt wurden.
Erinnern Sie sich daran, dass es Sinn und Zweck von Modularisierung ist,
Übersichtlichkeit, Wartbarkeit und Wiederverwendbarkeit zu fördern.
So sollte man statt dem Namen "Sauerkraut" irgendwie erwähnen, dass
es sich dabei um eine Farbe handelt. Genauso enthät der Name
"Wackelpudding" keine Information darüber, dass es sich um eine
Verschiebung handelt. Genauso sind die Namen "Schnitzel", "O_Saft",
"Kartoffelpuffer" und auch der Dateiname "mittagessen.wrl" Unfug.
Schauen Sie sich noch mal die Datei "mittagessen.wrl" an und philosophieren
Sie über bessere Namen.
Leider werden sind Fehlermeldungen in Cosmoplayer ausgesprochen
unauffällig, im folgenden Programmierteil werden sie aber
ausgesprochen wichtig, da Fehler beim Programmieren sehr
häufig vorkommen.
Leuchtet im Navigationsbalken von Cosmoplayer eine Reihe roter Lämpchen
auf

so bedeutet das, dass Cosmoplayer auf einen Fehler gestossen ist.
Klickt man auf die Lämpchen, öffnet sich die sogenannte
VRML Console, die die Fehlermeldung anzeigt.

Die gezeigte Fehlermeldung "# File is empty" ist sehr charakteristisch.
Sie zeigt an, dass der vorgeschriebene VRML Header
#VRML V2.0 utf8
nicht am absoluten Anfang des File gefunden wurde.
Leuchtet im Navigationsbalken von Cosmoplayer eine Reihe gelber Lämpchen
auf

so bedeutet das, dass Cosmoplayer auf eine Warnung gestossen ist.
Die VRML Console lässt sich auch mit der "\" Taste öffnen.
Genauer lässt sich das Verhalten der VRML Console einstellen, indem
man im Navigationsbalken auf das Häckchen klickt

und im sich öffnenden Fenster auf den Reiter "Advanced" klickt.

Während Fehler und "print" Ausgaben bei in "javascript" geschriebenen
Scriptknoten bei Cosmoplayer in der VRML Console erscheinen, werden
Fehler und "println" Ausgaben bei in java geschriebenen Scriptknoten
in die java Console geschrieben. Bei netscape 4.7X ist sie über
Communicator -> Tools -> Java Console erreichbar.
Das in einen VRML Browser eingebaute "javascript" wird auch als
"VRML-Script" bezeichnet, da die HTML spezifischen Befehle von
"javascript" im
VRML-Browser nicht funktionen (sollten) und gegen VRML spezifischen Befehle
ausgetauscht wurden. Offiziell heisst diese
Computersprache "ECMA-Script", nach dem gleichnamigen Normierungsgremium der
Europäischen Gemeinschaft.
Die wichtigsten Konstruktionen in "javascript" sind:
Verzweigungsbefehle:
haben die typische Form
if (rechte_seite1 vergleichsoperator rechte_seite2)
{
irgendwelche_befehle1;
irgendwelche_befehle2;
usw.
}
else
{
irgendwelche_befehle3;
irgendwelche_befehle4;
usw.
}
Wobei der gesamte else-Zweig weggelassen werden kann.
Dabei wird abhängig von Ergebnis des ersten Befehls (wahr oder falsch)
entweder der folgende oder der else-Zweig ausgeführt.
Typische Vergleichsoperatoren sind:
-
== gleich
-
!= ungleich
-
< kleiner
-
<= kleiner gleich
-
> grösser
-
>= grösser gleich
Für rechte_seite1 und rechte_seite2 gilt das bei
rechte_seite der Zuweisungsanweisung Gesagte.
Ein typisches Beispiel ist:
if (value < 0.5)
{
switch_wert=0;
}
else
{
switch_wert=1;
}
Schleifen:
haben die typische Form:
for ( anfangsbefehl ; vergleich ; inkrementbefehl )
{
irgendwelche_befehle5;
irgendwelche_befehle6;
usw.
}
Dabei ist anfangsbefehl der Befehl, der beim Betreten der
Schleife ausgeführt wird, vergleich ein Vergleich wie bereits
bei der Verzweigung kennengelernt um zu Entscheiden, ob die Schleife
abgebrochen wird und inkrementbefehl der Befehl, der nach jedem
Schleifendurchlauf weiterschaltet.
Ein typisches Beispiel ist:
for (i=0;i<3;i=i+1)
{
vec3f1[i]=vec3f2[i];
}
Das entspricht den einzelnen Zuweisungen
vec3f1[0]=vec3f2[0];
vec3f1[1]=vec3f2[1];
vec3f1[2]=vec3f2[2];
Um einen Scriptknoten mit Javascript zu erstellen ist sehr viel mehr
nötig, als die bisher kennengelernten Befehle.
Glücklicherweise ist Cosmoworlds in der Lage, einem den Hauptteil
dieser Arbeit abzunehmen.
Dabei generiert Cosmoworlds eine Vorlage mit dem Scriptknoten und startet
dann einen Texteditor, damit man die Vorlage weiter bearbeiten kann.
Dabei überlässt Cosmoworlds dem Benutzer die Wahl des Editors
über die Systemvariable $WINEDITOR bzw. $EDITOR.
Im schlimmsten Fall könnte Cosmoworlds den Editor vi benutzen,
falls die Systemvariable $EDITOR z.B. vom Systemadministrator entsprechend
gesetzt wurde.
Der Editor vi hat zwar den grossen Vorteil, dass er auf wirklich
auf so gut wie jedem Computersystem (auch über die Unix/PC oder
Homecomputerwelt hinaus) läuft, allerdings ist seine Bedienung ohne
weitere Einweisung so gut wie unmöglich.
Setzen Sie ihren graphischen Lieblingseditor (hier nedit, auch
z.B. xedit oder xemacs sind hier möglich) mit dem Befehl
setenv WINEDITOR nedit
bzw.
export WINEDITOR=nedit
und überprüfen Sie ihre Wahl mit dem Befehl
echo $WINEDITOR
Für ein kleines Beispiel beginnen wir mit einem TimeSenor.
Schneiden Sie
#VRML V2.0 utf8
TimeSensor {}
aus, schreiben sie es in eine ".wrl" Datei
und laden Sie es
mit File -> Import in Cosmoworlds.
Klicken Sie auf das Icon mit dem Scenengraph ("Open Outline Editor")
und überprüfen Sie das Ergebnis.
Klicken Sie auf das Script Icon ("Open Script Editor").
Es erscheint
Klicken Sie auf "Create Global", um ein neues Script zu erzeugen.
Klicken Sie dann auf "Add" um einen neuen Fieldname zu erzeugen.
Jetzt müssen Sie Eventinformation, Datentyp und den Fieldname
eingeben. Wir entschliessen uns, fraction vom TimeSensor
zu verarbeiten (eventIn). Wie wir im VRML Standard unter
http://tecfa.unige.ch/guides/vrml/vrml97/spec/part1/nodesRef.html#TimeSensor
nachschlagen können, benötigen wir dann
SFFloat als Datentyp.
Beim nächsten Klicken auf "Add" entschliessen wir uns, translation von
Transform (
http://tecfa.unige.ch/guides/vrml/vrml97/spec/part1/nodesRef.html#Transform
als Ziel (eventOut) zu benutzen (Datentyp SFVec3f).
Nach dem Klicken auf "Edit Script"
öffnet sich ein Fenster mit der Vorlage mit dem Scriptknoten.
Die Werte des eventIn meinFraction stehen jetzt in der Variable
value der Funktion meinFraction, die Ausgabewerte
zum eventOut meintranslation werden nach meintranslation[0],
meintranslation[1] und meintranslation[2] erwartet,
da es sich dabei um den Datentyp SFVec3f (3 Werte) handelt.
Wir entschliessen uns für eine Verzweigung und für einige
Mathematische Funktionen
und speichern ab.
Danach drücken wir "Parse Script" um Fehler zu finden.
Das ist nicht der Fall.
Um einen vernünftigen Transformknoten als Ziel zu haben,
müssen wir noch einen Körper einfügen.
Nun müssen nur noch die Routes in den Szenengraph eingetragen
werden und den Fieldname loop des TimeSensors auf TRUE gesetzt werden,
damit der TimeSenor aktiv bleibt.
Danach kann man das Ergebnis über File -> Preview betrachten.