Introduzione al 3D in ActionScript
La principale differenza tra un oggetto bidimensionale (2D) e un oggetto tridimensionale (3D) in flash sta’ nel fatto che l’oggetto 3d e’ la proiezione di un oggetto caratterizzato dalle 3 dimensioni (altezza, larghezza e lunghezza) su uno schermo bidimensionale quindi e’ da considerare l’aggiunta di una terza dimensione per l’oggetto che si vuole rappresentare . La terza dimensione consente di spostare un oggetto verso e lontano dal punto di vista del utente
Quando si imposta esplicitamente la proprieta’ z di un displayobject in un valore numerico, l’oggetto crea automaticamente una matrice di trasformazione 3D. e’ possibile modificare tale matrice cambiando le impostazioni di trasformazione 3D di tale oggetto.
In una rappresentazione 2D l’asse Z di rotazione e’ perpendicolare al piano x y, dunque allo stage mentre In una rappresentazione 3D l’asse di rotazione puo’ essere attorno a uno qualsiasi degli assi di rotazione x, y, z. L’impostazione delle proprieta’ di rotazione e ridimensionamento di un displayobject consentono a questi di muoversi nello spazio 3D.
Nei prossimi capitoli della guida affronteremo i seguenti argomenti:
1 - Creazione di un oggetto 3D
2 - Spostamento di un oggetto in uno spazio tridimensionale
3 - Rotazione di un oggetto in uno spazio tridimensionale
4 - Rappresentare della profondita’ utilizzando la prospettiva di proiezione
5 - Riordino della display list in corrispondenza alla relativa posizione degli oggetti rispetto all’asse-z in modo che essi appaiono di fronte al altro correttamente per l’utente
6 - Trasformare oggetti 3D utilizzando matrici 3D
7- Utilizzo di vettori per manipolare gli oggetti nello spazio 3D
8 - Utilizzo del metodo Graphics.drawTriangles () per creare prospettive
9 - Utilizzo della mappatura UV per aggiungere bitmap texture ad un oggetto 3D
10 - Impostazione del parametro culling del metodo Graphics.drawTriangles () al fine di velocizzare il rendering e nascondere parti di un oggetto 3D che si trovano fuori dal punto di vista. Del - - utente
termini e concetti :
Il seguente elenco di riferimento contiene termini e concetti che si incontreranno in questa guida:
prospettiva: in un piano 2D, la rappresentazione di linee parallele convergenti in un punto di fuga per dare l’illusione della profondita’ e distanza
proiezione: la rappresentazione bidimensionale di un oggetto 3d
rotazione: cambiare l’orientamento (e spesso la posizione) di un oggetto muovendo ciascun punto incluso nell oggetto in un movimento circolare
trasformazione: Alterazione di punti 3d o un insiemi di punti 3d mediante rotazione,traslazione scale e skew o combinazione di queste azioni
traslazione: cambiare la posizione di un oggetto muovendo tutti i punti inclusi, nella stessa distanza e nella stessa direzione
punto di fuga: e’ quel punto in cui linee parallele sembrano incontrarsi quando sono rappresentate in una prospettiva lineare
vettore: un vettore 3D rappresenta un punto o una posizione nel spazio tridimensionale utilizzando le coordinate cartesiane, x, y, z e
vertice: un punto angolare
textured mesh: insieme di punti che definiscono la superficie di un oggetto nello spazio 3D .
UV mapping: un modo per applicare una texture o bitmap ad una superficie 3D. L’ UV mapping assegna valori di coordinate su una immagine come percentuali del orizzontale (U) e l’asse verticale (V) .
Creazione,spostamento e rotazione di un oggetto in uno spazio tridimensionale:
In questa prima parte della guida vedremo come flash player 10 gestisce lo spostamento e la rotazione di un displayObject in uno spazio 3d, come abbiamo visto precedentemente la sostanziale differenza con le precedenti versioni del flash player sta’ nel aggiunta di un asse di spostamento perpendicolare allo stage di visualizazzione detto asse z, e’ importante anche notare , che nel sistema di assi x,y,z addotatto dal flash player l’incremento della proprieta’ z del displayObject determina un allontanamento dal punto di vista del osservatore mentre vengono mantenute le stesse convenzioni applicate al sistema di coordinate 2d, cioe’ un incremento della proprieta’ y determina uno spostamento verso il basso, mentre un incremento della proprieta’ x determina uno spostamento verso destra, un altro elemento importante da tenere in mente , consiste nel conflitto che puo’ sussistere tra , lo z-order (posizione di un oggetto 3d nella displaylist) e lo z-axis (cioe’ posizione di un oggetto 3d lungo l’asse z) . Per capire meglio questo concetto vediamo il seguente esempio:
(per testare gli esempi proposti , possiamo copiare gli script nella document class del nostro progetto in flash cs4, altrimenti se usiamo flex builder , aggiorniamolo alla versione 3.2 , creiamo un nuovo progetto actionscript class , dunque in project>properties, nel box HTML wrapper impostiamo come flash player predefinito la versione 10.0.0 )
package{
import flash.display.*;
import flash.events.*;
public class tred extends Sprite{
private var depth:int = 1000;
private var ellipse1Back:int = 1;
private var ellipse2Back:int = 1;
public function tred()
{
var ellipse1:Sprite = new Sprite();
ellipse1.graphics.beginFill(0xff0000);
ellipse1.graphics.drawEllipse(0,0, 100, 50);
ellipse1.x = 100;
ellipse1.y = 50;
ellipse1.z = 450;
this.addChild(ellipse1);
var ellipse2:Sprite = new Sprite();
ellipse2.graphics.beginFill(0×0000FF);
ellipse2.graphics.drawEllipse(0,0, 100, 50);
ellipse2.x = 100;
ellipse2.y = 50;
ellipse2.z = 500;
this.addChild(ellipse2);
}
}
}
se testiamo il seguente script vedremo che l’ ellise rossa nonostante sia piu’ vicina al punto di osservazione rispetto al ellisse blu’ , sembra invece nascondersi sotto l’ ellisse blu’ , questo e’ dovuto al fatto che nonostante sia piu’ vicina al osservatore rispetto al ellisse blu’ ,lo z-order di quest’ ultima e’ maggiore, Questo non corrisponde a cio’ che vogliamo rappresentare, La soluzione a questo problema sara’ trattato in seguito,
Vediamo inanzitutto quali sono le classi coinvolte per il supporto al 3d del flash player 10 :
- flash.display.DisplayObject : oltre alla proprieta’ z che abbiamo visto PRECEDENTEMENTE , sono state aggiunte le proprieta’ rotationX, rotationY e rotationZ che consentono di ruotare i displayobject lungo i 3 assi cartesiani X,Y,Z .
- flash.geom.Vector3D : Questa puo’ essere usata come struttura dati per gestire i punti 3D . inoltre consente di gestire i vettori tridimensionali
- flash.geom.Matrix3D :Quando aggiungiamo le proprieta’ z,rotationX,rotationY o rotationZ, viene istanziato un oggetto della classe transform che contiene la proprieta’ Matrix3D , che a sua volta controlla la rappresentazione di un displayObject nello spazio 3D, vediamo un esempio:Impostiamo le seguenti proprieta’ del displayObject “my3dObjec”
my3dObject.x = 100; my3dObject.y = 50; my3dObject.z = -30;
Estraiamo la propieta’ matrix3d del oggetto transform associato a my3dObjec ed analizziamola
var my3dObjectMatrix:Matrix3D = my3dObject.transform.matrix3D; trace(my3dObjectMatrix.position.x);
trace(my3dObjectMatrix.position.y);
trace(my3dObjectMatrix.position.z);
trace(my3dObjectMatrix.position.length);
trace(my3dObjectMatrix.position.lengthSquared);
- flash.geom.PerspectiveProjection class controlla la proiezione di un oggetto 3d in un piano bidimensionale
Ci sono 2 differenti approcci per la rappresentazione di Immagini 3d nel flash player 10:
- Usando le proprieta’ x, y e z dei display objects, o settando la rotazione e le proprieta’ di scalatura usando la DisplayObject class. Altrimenti gestendo l’oggetto DisplayObject.transform.matrix3D ci consente di implementare animazioni ed effetti piu’ complessi. L’oggetto DisplayObject.transform.perspectiveProjection personalizza come i display object vengono ridisegnati in una prospettiva 3D. Si utilizza generalmente questo approccio quando si vogliono generalmente rappresentare superfici planari. Ad esempio gallery di immagini 3d o rappresentare animazioni bidimensionali in uno spazio 3d
- Generando mesh poligonali triangolari.Per usare questo approccio, si devono innanzitutto definire e gestire i dati di oggetti 3D e convertire questi dati in triangoli 2D per il rendering delle immagini. Le Bitmap textures (immagini raster che rivestono la superficie di un oggetto 3d) possono essere mappate a questi triangoli, dopo i triangoli sono disegnati in un oggetto grafico usando il metodo Graphics.drawTriangles().Esempio di quest’approccio puo’ essere il caricamento dei dati di un oggetto 3d da un file xml e la renderizazzione di questo nello schermo.
In questa prima parte della guida utilizzeremo il primo approccio, riprendendo quanto accennato precedentemente , analizziamo adesso una tecnica che viene generalmente utilizzata per riordinare la displaylist di un displayObjectContainer sulla base dello z-axis (coordinata z) dei displayObject in esso contenuto, diamo un occhiata alla classe SimpleZSorter di Ralph Hauwer scaricata dal Blog di Lee Brimelow .
ho ulteriormente commentato il seguente codice:
package com.theflashblog.fp10 {
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.geom.Matrix3D;
/*** @author Ralph Hauwert / UnitZeroOne*/
public class SimpleZSorter
{
/**
analizziamo i parametri che vengono passati alla function sortClips(container, recursive);
container e’ il displayObjectContainer contenente i displayObjects che dovranno essere ordinati in base al loro z-order
la variabile booleana recursive se settata a true, la function sortClips sar?iterata ricorsivamente a tutti i display objects annidati.
*/
public static function sortClips(container : DisplayObjectContainer, recursive : Boolean = false) : void
{
//verifichiamo che viene passato un parametro
if(container != null){
//verifichiamo che il displayObjectContainer contiene childs annidati
var nc:int = container.numChildren;
if(nc > 1){
var index:int = 0;
// vo questo oggetto consente di archivare lo z-axis e il riferimento aldisplayObject che deve essere ordinato nella displaylist
var vo : SimpleZSortVO;
var displayObject:DisplayObject;
var transformedMatrix : Matrix3D;
//mainparent contiene il riferimento alla root del container, questo displayObject deve essere passato come parametro
var mainParent : DisplayObject = traverseParents(container);
//questo array viene utilizzato per memorizzare i displayObject che verranno ordinati secondo la proprieta’ screenZ definita nella classe SimpleZSortVo
var sortArray : Array = new Array();
//visitiamo dunque tutti i displayObject contenuti in container
for(var c:int = 0; c < container.numChildren; c++){
displayObject = container.getChildAt(c);
//possiamo anche decidere di iterare la funzione ricorsivamente su tutti i child contenuti in displayObject
if(recursive && (displayObject is DisplayObjectContainer)){
sortClips(displayObject as DisplayObjectContainer, true);
}
//otteniamo un riferimento alla matrice di trasformazione 3d del displayObject che deve essere ordinato, da questa possiamo ottenre lo z-axis dell oggetto
transformedMatrix = displayObject.transform.getRelativeMatrix3D(mainParent);
//quindi memorizziamo il displayObject e la il relativo z-axis nell oggetto SimpleZSortVO, e aggiungiamolo nel array
sortArray.push(new SimpleZSortVO(displayObject, transformedMatrix.position.z)
);
}
//Quindi ordiniamo l’array in base alla propriet? screenZ (z-axis) dei displayObjects ivi memorizzati
sortArray.sortOn(”screenZ”, Array.NUMERIC | Array.DESCENDING);
for each(vo in sortArray){
//Quindi settiamo gli indici dei displayObjects in accordo con i loro rispettivi z-axis
container.setChildIndex(vo.object, index++);
}
//cancelliamo l’array.
sortArray = null;
}
}else{
throw new Error(”No displayobject was passed as an argument”);
}
}
/**
* questa funzione consente di ottenere il riferimento al oggetto root principale del container.
*/
private static function traverseParents(container : DisplayObject) : DisplayObject
{
//otteniamo il riferimento al corrente nodo parent.
var parent : DisplayObject = container.parent;
var lastParent : DisplayObject = parent;
//il ciclo viene iterato finche’ il valore del parent e’ nullo
while((parent = parent.parent) != null){
lastParent = parent;
}
//Return the “top most” parent.
return lastParent;
}
}
}
questo e’ il codice della classe SimpleZSortVO che utilizziamo per la memorizazzione dei displayObjects e i relativi z-axis
package com.theflashblog.fp10 {
import flash.display.DisplayObject;
/**
* @author Ralph Hauwert / UnitZeroOne
*/
public class SimpleZSortVO
{
public var object : DisplayObject;
public var screenZ:Number;
public function SimpleZSortVO(object : DisplayObject, screenZ:Number){
this.object = object;
this.screenZ = screenZ;
}
}
}
vediamo adesso come possiamo animare le proprieta’ di un displayObject 3D.
Qui, un esempio che usa il gestore di eventi ENTER_FRAME:
package {
import flash.display.*;
import flash.events.*;
public class flash3dtmp extends Sprite{
//impostiamo il valore massimod ella profondita’
private var depth:int = 1000;
private var ellipse1Back:int = 1;
private var ellipse2Back:int = 1;
public function flash3dtmp()
{
// Creiamo 2 ellissi
var ellipse1:Sprite = new Sprite();
ellipse1.graphics.beginFill(0xff0000);
ellipse1.graphics.drawEllipse(0,0, 100, 50);
ellipse1.x = 100;
ellipse1.y = 50;
this.addChild(ellipse1);
var ellipse2:Sprite = new Sprite();
ellipse2.graphics.beginFill(0×0000FF);
ellipse2.graphics.drawEllipse(0,0, 100, 50);
ellipse2.x = 300;
ellipse2.y = 150;
this.addChild(ellipse2);
Aggiungiamo ad entrambi il listener Event.ENTER_FRAME
ellipse1.addEventListener(Event.ENTER_FRAME, ellipse1FrameHandler);
ellipse2.addEventListener(Event.ENTER_FRAME, ellipse2FrameHandler);
}
private function ellipse1FrameHandler(e:Event):void
{
// invertiamo la rotta dello spostamento z ogni qualvolta vengono raggiunti i klimiti di profondita’
ellipse1Back = setDepth(e, ellipse1Back);
e.currentTarget.z += ellipse1Back * 10;
}
private function ellipse2FrameHandler(e:Event):void
{
ellipse2Back = setDepth(e, ellipse2Back);
e.currentTarget.z += ellipse2Back * 20;
}
private function setDepth(e:Event, d:int):int
{
//se la proprieta’ z dell ellisse e’ maggiore di depth o inferiore di 0 viene invertito lo spostamento
if(e.currentTarget.z > depth)
{
e.currentTarget.z = depth;
d = -1;
}
else if (e.currentTarget.z < 0)
{
e.currentTarget.z = 0;
d = 1;
}
return d;
}
}
}
e’ possibile implemtare animazioni basate su Tweener come le Caurina Transitions (http://tweener.googlecode.com/files/tweener_1_31_74_as3.zip) ,
nell ‘esempio seguente istanziamo un container nel quale aggiungiamo un numero definito di oggetti grafici , distribuendoli a caso in uno spazio 3D:
Dunque cliccando su ciascun sprite, il container si spotera’, in modo tale da focalizzare lo sprite selezionato.
package
{
import flash.display.*;
import flash.events.*;
// importiamo la libreria
import caurina.transitions.*;
public class tred extends Sprite
{
private var cont:Sprite;
public function tred():void
{
// istanziamo il container che conterra’ la nostra scena 3d
cont = new Sprite();
cont.x = stage.stageWidth * 0.5;
cont.y = stage.stageHeight * 0.5;
addChild(cont);
makeChildren();
}
//Disponiamo nel container in maniera casuale un numero definito di sprites a partire dal piu’ profondo, questo ci consente die vitare un eventuale conflitto con lo z-order degli sprites comeabbiamo visto precedentemente.
private function makeChildren():void
{
for (var i:uint=100; i>0; i–)
{
var sp:Sprite = new Sprite();
sp.alpha = 0.8;
sp.addEventListener(MouseEvent.CLICK, onClick);
sp.graphics.beginFill(Math.random() * 0xFFFFFF);
sp.graphics.drawRect(0, 0, 300, 300);
sp.x = Math.random() * 1000 - 500;
sp.y = Math.random() * 1000 - 500;
sp.rotationY = Math.random() * 45;
sp.z = i * 200;
cont.addChild(sp);
}
}
private function onClick(e:Event):void
{
var sp:Sprite = Sprite(e.target);
// al click del mouse sullo sprite selezionato , il container si sposter? in modo che lo sprite occupi tutta l’area dello stage
Tweener.addTween(cont,{x:-sp.x+150,y:-sp.y+75, z:-sp.z+100, time:1});
}
}
}
i prossimi argomenti che saranno trattati in questa guida sulle nuove 3D Features del flash player 10 saranno:
- Proiezione di oggetti 3D in una vista 2D
- La Matrix3D Class(introdotta precedentemente) che consente un maggiore controllo delletrasformazioni 3D
- Le mesh poligonali triangolari