PlaneUV-Klasse für Away3D

Wofür braucht man sowas?

Bei einem kürzlich fertig gestellten Flash-AS3-Projekt (dem OUTFLEXX-Loungeplaner) ging es darum, eine Reihe ähnlich aufgebauter Rattan-Möbel mittels Away3D (v3.6) räumlich darzustellen. Hier als Beispiel ein Sessel aus dieser Produktlinie:

OUTFLEXX-Sessel in Away3D-Darstellung

Da alle Möbel (etwas vereinfacht) aus kubischen Elementen bestehen, war die Modellierung mit Hilfe einfacher Planes in Away3D selbst möglich. Hier drei Planes einer Armlehne mit ihren Rattan-Texturen:

3 Planes mit Rattan-Textur für eine Armlehne

Bei der Erstellung dieser Rattan-texturierten Oberflächen-Planes mittels der regulären Away3D-Plane-Klasse bin ich dann auf zwei Probleme gestoßen:

1. Für jede Plane ist ein eigenes Bitmap-Material nötig
Wie aus dem obigem Bild ersichtlich ist, muss jede Plane eine eigene Rattan-Bitmaptextur in den richtigen Proportionen haben, damit das Größenverhältnis der Oberflächen zueinander stimmt. Verwende ich nur eine Rattan-Bitmap für alle Planes, wird diese Textur jedes Mal verzerrt. Bei den drei Flächen sähe das z.B. so aus:

Verzerrte Rattan-Texturen der 3 Planes

Deswegen wäre für jede der ca. 100 verschiedenen Plane-Größen im Loungeplaner ein eigenes Bitmap-Material mit wiederum einer eigenen Textur-Bitmap nötig, was sowohl die Dateigröße der swf-Datei als auch den Arbeitsspeicherbedarf zur Laufzeit entsprechend erhöht – obwohl ja im Prinzip immer dieselbe Rattan-Textur gezeigt wird, nur in verschiedenen Ausschnitten. Hier die Umrisse der Rattan-Texturausschnitte der drei Planes auf einer einzigen Textur-Bitmap vereint:

Alle 3 Rattan-Texturen als Ausschnitte einer Bitmap

2. Das Rattan-Material soll ausgetauscht werden können
Wenn der Benutzer im Loungeplaner die Rattanfarbe wechselt, muss das Rattanmaterial bei allen dargestellten Möbeln durch ein anderes ersetzt werden. Es müssten also pro weiterer Rattan-Farbe noch einmal so viele Bitmap-Materialien und Textur-Bitmaps angelegt werden. Mit denselben Folgen für Dateigröße und Speicherbedarf wie bei Punkt 1.

Um diese beiden Probleme zu umgehen, habe ich mir eine eigene PlaneUV-Klasse geschrieben, die als zusätzliche Parameter eigene UV-Koordinaten für die Textur entgegennimmt, so dass ich insgesamt mit nur zwei Rattan-Bitmaps (eine für schwarzes und eine zweite für braunes Rattan) auskomme. Beim Wechseln der Rattan-Farbe wird nur allen mit Rattan texturierten Planes das jeweils andere Bitmap-Material zugewiesen, die Möbel selbst müssen nicht neu aufgebaut werden.

Was sind die UV-Koordinaten einer Bitmap?

Das Verfahren, mehrere Texturen in einer Bitmap unterzubringen, nennt man „Texturatlas“ (oder auch „Spritesheet“ bei Sprite-Animationen bzw. „SubUV“ bei Partikelsystemen). Da die Rattan-Geschichte doch sehr speziell für mein Projekt ist, hier ein anschaulicheres Beispiel für einen solchen Texturatlas, der ganz verschiedene Oberflächen-Texturen in einer 512 x 512 Pixel großen Bitmap vereint:

Texturatlas-Bitmap

Diese Textur-Bitmap enthält ein paar willkürlich ausgewählte Ausschnitte aus alten Urlaubsfotos, die schwarze Tür in der oberen Hälfte werde ich als Beispiel zum texturieren einer PlaneUV verwenden.

Zuerst gilt es, die Koordinaten des Bitmap-Ausschnitts der Tür innerhalb des Gesamt-Bitmaps zu bestimmen. Die Koordinatenachsen einer Textur werden in der Computergrafik mit „u“ und „v“ bezeichnet. Sie entsprechen grob den „x“- und „y“-Koordinatenachsen, die in Flash z.B. in der Funktion getPixel32(x,y) verwendet werden,  mit 2 entscheidenden Unterschieden:

1. Der Ursprung der u/v-Koordinatenachsen liegt in der unteren linken Ecke, während der Ursprung des x/y-Koordinatensystems in der oberen linken Ecke liegt. Die u-Achse der u/v-Koordinaten entspricht also der x-Achse von Flash, aber die v-Achse läuft entgegen der y-Achse von Flash.

2. Die u/v-Koordinatenwerte entsprechen nicht der Anzahl der Pixel, sondern werden proportional zur Gesamtbreite/-höhe der Bitmap von 0.0 bis 1.0 jeweils in x- und y-Richtung berechnet. Links unten ist also u= 0.0 / v = 0.0 und die rechte obere Ecke hat die u/v-Koordinaten u = 1.0 / v = 1.0. Diese relativen Koordinatenwerte haben den Vorteil, dass sie unabhängig von der absoluten Bitmapgröße sind. Die zugrunde liegende Textur-Bitmap kann jederzeit durch eine größere oder kleinere ausgetauscht werden, ohne dass sich diese u/v-Koordinatenwerte ändern.

Wie werden die benötigten UV-Koordinaten berechnet?

Um die u/v-Koordinaten für den Bereich der schwarzen Tür in meinem Urlaubsbild-Texturatlas zu bestimmen, müssen die absoluten x/y-Pixelwerte der Textur-Bitmap in relative u/v-Koordinaten umgerechnet werden. In absoluten Pixelwerten hat die Tür, ausgehend von der linken unteren Ecke, diese Position und Abmessungen:

Texturatlas-Bitmap mit Vermaßung der schwarzen Tür

Um daraus die u/v-Koordinaten zu errechnen, müssen wir nur die ermittelten Pixelwerte durch die gesamte Breite bzw. Höhe der Bitmap teilen:
u-Start = 211 / 512 = 0.412
u-Breite = 186 / 512 = 0.363
v-Start = 191 / 512 = 0.373
v-Höhe = 321 / 512 = 0.627

Wie wird die PlaneUV-Klasse verwendet?

Am Ende des Artikels gibt’s den Sourcecode der PlaneUV-Klasse zum Download, zusammen mit einer Flash-Beispieldatei im CS4-Format. Das Away3D-Framework (bei mir v3.6, andere Versionen müssten aber auch funktionieren) muss natürlich ebenfalls installiert sein.

Um die schwarze Tür auf einer PlaneUV darzustellen, benötigen wir
• ein Bitmap-Material mit der BitmpaData des Texturatlas.jpg (exportiert für Actionscript als “texturatlas”):

bitmapMaterial = new BitmapMaterial(Cast(texturatlas));

• eine PlaneUV, der die u/v-Koordinaten übergeben werden. Dies ist entweder direkt bei der Erstellung der PlaneUV möglich oder später durch Zuweisung der u/v-Koordinaten:

1. Wenn die Übergabe sofort bei der Erstellung der PlaneUV erfolgt, müssen die u/v-Koordinaten dem ini-Object als Array [u-Start, v-Start, u-Breite, v-Höhe] in der Property „uvRect“ übergeben werden:

iniObject:Object = { uvRect: [ 0.412, 0.373, 0.363, 0.627 ] };
tuer = new planeUV(iniObject);

2. Wenn die u/v-Koordinaten erst nach der Erstellung der Plane zugewiesen (oder auch geändert) werden sollen, müssen sie in Form eines Rectangles (u-Start, v-Start, u-Breite, v-Höhe) der PlaneUV-Property „uvRect“ zugewiesen werden:

tuer = new planeUV();
tuer.uvRect = new Rectangle(0.412, 0.373, 0.363, 0.627);

Der Grund, warum ich einmal ein Array und einmal ein Rectangle verwende, ist der, dass in meiner PlaneUV-Klasse die u/v-Koordinaten intern als Rectangle gespeichert werden (und deshalb die getter/setter-Methoden für „uvRect“ ein Rectangle  liefern/erwarten). Da das ini-Objekt von Away3D aber keine Funktion „ini.getRectangle()“ kennt, um ein Rectangle aus dem Init-Objekt zu extrahieren (und ich nicht auch noch die away3d.core.utils.init-Klasse erweitern wollte), habe ich dort stattdessen die vorhandene „ini.getArray()“-Funktion benutzt.

Die „tuer“ -PlaneUV muss dann noch die korrekte Größe bekommen, damit die Tür-Textur auch möglichst unverzerrt dargestellt wird:

tuer.width = 290;
tuer.height = 500;

Das Ergebnis sehen wir hier (die PlaneUV habe ich anschließend noch etwas gedreht, damit der 3D-Charakter besser zur Wirkung kommt):

Get Adobe Flash player

Die PlaneUV-Klasse kann frei verwendet werden, falls sie jemand für sein Projekt gebrauchen kann:

Souce-Code zum Download als ZIP-Datei DOWNLOAD SOURCE

Veröffentlicht in Away3D, Deutsch, Flash | Keine Kommentare »

Kommentar verfassen

Achtung: Die Kommentare werden moderiert und erscheinen deshalb nicht sofort im Blog!