
De las tres opciones para gráficos 3D en Flash que mencionamos en la entrada anterior, la primera que vamos a presentar con detalle es Papervision3D.
Una característica interesante del proyecto (y que comparte con los otros proyectos 3D que analizaremos más adelante) es que es software libre. En concreto, Papervision se publica bajo una licencia MIT, que está considerada una licencia open source. Este detalle es especialmente interesante para nosotros porque, al tener acceso al código fuente, podemos leer directamente el código para averiguar cómo usa exactamente la biblioteca algunas propiedades de clase y parámetros de métodos que por desgracia no están descritos en la documentación oficial de las clases.
El sitio web principal de Papervision es papervision3d.org (la intro Flash de esta página es en sí misma un buen ejemplo de Papervision, aunque demasiado pesado). Si embargo, el grueso de los recursos —código fuente, binarios, documentación, etc.— se encuentra en la página correspondiente de Google Code. La última versión empaquetada de la biblioteca es la 2.1.932 y el SWC correspondiente ocupa unos 485 KB. La URL del repositorio Subversion con el código fuente es http://papervision3d.googlecode.com/svn/trunk.
Un holamundo en Papervision3D — Flash, fuente
Veamos cuáles son los elementos mínimos que deben existir en cualquier mundo 3D (esta terminología es común a la mayoría de sistemas):
- Vista. Es el componente que sirve de base y que va a contener al resto de elementos.
- Escena. Son todos los objetos y efectos que se van a mostrar, o sea la definición del entorno en tres dimensiones.
- Cámara. Obviamente, se trata de una cámara virtual; una abstracción que nos permite introducirnos en el mundo 3D y observarlo desde un determinado punto de vista.
- Visor (viewport). Podemos pensar en el visor como en una pantalla de cine, o de televisión. Es la superficie plana de forma rectangular sobre la que se proyecta la imagen.
- Motor (renderer). Es el proceso que hace todos los cálculos necesarios para generar sobre el visor una representación de la escena tal y como se vería desde la cámara.
Como primer contacto con Papervision, vamos a escribir un ejemplo lo más pequeño posible en Flex 4. Escribiremos dos clases: una que llamaremos Papervision3DWorld y que se ocupará de crear y de animar un pequeño mundo 3D, y la típica clase principal (subclase de spark.components.Application) que se limitará a instanciar a Papervision3DWorld.
Como ya hemos dicho, el componente principal a través del cual vamos a ver el mundo 3D que creemos es la vista. En Papervision, las vistas son subclases de flash.display.Sprite. En Flex 4 no es posible añadir un hijo a Container mediante addChild(child:DisplayObject), así que no podremos añadir una vista directamente a Application. Lo que vamos a hacer es envolver la vista en un UIComponent, e instanciar este último dentro de la aplicación. (Mantendremos este mismo enfoque para las otras dos bibliotecas que veamos más adelante).
La escena que vamos a crear es un simple cilindro gris que va a rotar indefinidamente frente a nosotros (siga el enlace del pantallazo para ver el ejemplo en acción; puede hacer clic con el botón derecho sobre la película Flash para ver el código fuente y descargarlo):

Para hacer esto, podríamos crear y configurar a mano los cinco elementos mencionados arriba. Sin embargo, Papervision facilita la creación de vistas simples mediante una vista ya «precocinada»: basta con instanciar la clase BasicView con unos pocos parámetros. Entre esos parámetros, y dado que esto es una prueba básica, vamos a especificar una cámara de tipo depuración, que tiene la ventaja de que nos permitirá algunos movimientos con el teclado y el ratón. Añadimos la vista como hija de Papervision3DWorld:
private var _view:BasicView;
_view = new BasicView (width, height, true, true, CameraType.DEBUG);
addChild (_view);
Cualquier objeto que se cree en Papervision (incluyendo objetos especiales como una cámara) se coloca por defecto en el centro del sistema de coordenadas. Como pensamos dejar el cilindro en (0, 0, 0), vamos a alejar la cámara 1000 unidades hacia atrás en el eje z (ese es el eje que va perpendicular a nuestra pantalla, hacia dentro de ésta). También vamos a ajustar el campo visual de la cámara a 60°.
_view.camera.z = -1000;
_view.camera.fov = 60;
A continuación, vamos a crear el cilindro. Como la mayoría de sistemas de modelado y visualización 3D, Papervision nos ofrece unas pocas «primitivas»: objectos simples, generalmente poliedros, que podemos usar como bloques con los que construir escenas más complejas. Vamos a usar la primitiva Cylinder (que es en realidad un tronco de cono) dándole 200 unidades de radio tanto en la base como en la parte superior, y 800 unidades de altura. El resto de parámetros definen el material (un color plano) y el número de polígonos que se usarán para aproximar las superficies planas y curvas del objeto. Si no rotamos el cilindro, lo veremos de frente y en vertical, como si estuviésemos delante de la columna de un edificio. Para poder ver sus bases y hacerlo más interesante, lo rotamos sobre dos ejes. El último paso es añadir el cilindro a la escena (que, recordemos, es parte de la vista).
private var _object:DisplayObject3D;
_object = new Cylinder (new FlatShadeMaterial (null), 200, 800, 100, 1, 200, true, true);
_object.rotationX = 30;
_object.rotationZ = 60;
_view.scene.addChild (_object);
Finalmente, para que el objeto se mueva necesitamos alterar su orientación ligeramente cada vez que Flash dibuje un cuadro nuevo, y pedirle explícitamente a Papervision que renderice de nuevo la vista para reflejar los cambios en la escena. Esto se consigue asociando un manejador al evento enterFrame:
addEventListener (Event.ENTER_FRAME, render);
private function render (e:Event): void {
_object.rotationY += 1;
_view.singleRender ();
}
El cuadro negro de la parte superior izquierda es otra consecuencia de usar org.papervision3d.cameras.DebugCamera3D: muestra la posición y la orientación de la cámara, así como algunas otras propiedades de ésta. Pruebe a hacer clic sobre la escena y a usar el ratón y el teclado para moverse dentro de ella.
Pruebas de rendimiento con Papervision3D — Flash, fuente

El holamundo que acabamos de escribir incluye sólo un objeto con pocos polígonos. En concreto, si miramos los valores que pasamos como parámetros al constructor del cilindro, veremos que la superficie curva se está aproximando con n=100 segmentos. Ese nivel de detalle produce n×2=200 triángulos recubriendo toda la altura del cilindro, mientras que cada una de las dos bases está compuesta por n-2=98 triángulos. Esto arroja un total de 200+(2×98)=396 triángulos.
La forma en la que un objeto como este se aproxima usando únicamente triángulos queda más clara si cambiamos un poco la llamada al constructor del cilindro para asignarle un material de tipo alambre negro y bajamos mucho el número de segmentos para simplificar la figura:
_object = new Cylinder (new WireframeMaterial (0, 1, 2), 200, 800, 10, 1, 200, true, true);
Vamos a poner a prueba a Papervision3D aumentando el número de polígonos: crearemos 400 objetos dispuestos en un cuadrado de 20×20 objetos. Seguiremos usando la primitiva Cylinder, pero esta vez reduciremos el número de segmentos a n=4, obteniendo en la práctica prismas de base cuadrada.
Siguiendo la fórmula anterior, cada prisma estará compuesto por 4×n-4=12 triángulos. En total, 400×12=4800 triángulos en la escena. A cada objeto le asignaremos un color ligeramente diferente, creando un degradado, y una rotación inicial también distinta (siga el enlace del pantallazo para ver la prueba de rendimiento en acción; puede hacer clic con el botón derecho sobre la película Flash para ver el código fuente y descargarlo):

Para medir la eficiencia del resultado, usaremos la clase StatsView, que muestra en tiempo real algunas estadísticas internas de Papervision:
private var _stats:StatsView;
_stats = new StatsView (_view.renderer);
addChild (_stats);
En este caso, en lugar de una propiedad _object de tipo DisplayObject3D, tendremos una propiedad _objects de tipo ArrayCollection. Cada prisma que se crea se añade tanto al vector _objects como a la escena:
private var _objects:ArrayCollection;
var cylinder:DisplayObject3D;
var colour:uint;
_objects = new ArrayCollection ([]);
for (var i:int = -475; i <= 475; i += 50) {
for (var j:int = -475; j <= 475; j += 50) {
colour = (((i + 475) * 191 / 950 + 64) << 16) +
(((j + 475) * 191 / 950 + 64) << 8);
cylinder = new Cylinder (new FlatShadeMaterial (null, colour, 0x808080),
10, 40, 4, 1, 10, true, true);
cylinder.x = i;
cylinder.y = j;
cylinder.rotationX = (i + 475) * 360 / 950;
cylinder.rotationY = (j + 475) * 360 / 950;
_objects.addItem (cylinder);
_view.scene.addChild (cylinder);
}
}
El tipo de cámara, así como su posición y orientación, son distintas a las del programa anterior para mejorar un poco la apariencia y garantizar que todos los objetos de la escena son visibles dentro del visor.
El último cambio es que el método render ahora necesita recorrer todo el vector de objetos para actualizar sus orientaciones uno por uno:
private function render (e:Event): void {
for each (var cylinder:DisplayObject3D in _objects) {
cylinder.rotationX += 4;
cylinder.rotationY += 4;
}
_view.singleRender ();
}
Como vemos, el número de cuadros por segundo que Papervision es capaz de manejar ha bajado notablemente al complicar la escena. En próximos artículos prestaremos atención a las estadísticas que muestra StatsView y analizaremos la bondad de esta prueba de rendimiento en relación a otras biblioteca Flash para 3D.
Próxima entrega: Away3D
Tags: 3D, análisis, analysis, benchmark, biblioteca, ejemplo, example, flash, flex, hello, helloworld, hola, holamundo, howto, introducción, introduction, library, mundo, Papervision3D, prueba, rendimiento, tutorial, world