inmensia |
Blog
Juan Mellado, 21 Febrero, 2012 - 09:13
Entradas del cuaderno de bitácora de los últimos tres días: - Encontrado workaround para antialias en transformaciones CSS 3D en navegador Firefox ¿Cómo diablos se refleja todo esto en un curriculum?
Juan Mellado, 16 Febrero, 2012 - 12:41
He añadido un nuevo método público a js-lzma para que pueda descomprimir directamente un fichero generado con el compresor de línea de comandos que viene con el SDK. js-lzma es un librería que escribí en JavaScript para poder descomprimir ficheros en formato LZMA. Originalmente obligaba a leer el fichero y extraer la cabecera por parte del cliente. Hoy lo que he hecho es añadir una función en la interface pública que ya lo hace todo por si sola. Con la función original hay que realizar la llamada de esta forma: LZMA.decompress(properties, inStream, outStream, outSize);Con la nueva función basta con hacerlo de esta forma: LZMA.decompressFile(inStream, outStream);He aprovechado para actualizar la licencia, porque buscando por la web he visto que algunos proyectos están empezando a utilizarla.
Juan Mellado, 15 Febrero, 2012 - 13:54
En mis últimos proyectos he venido utilizando gl-matrix, una librería escrita en JavaScript para operar con vectores, matrices y quaterniones. Es obra de Brandon Jones, un ingeniero que trabaja para el área de móviles de Motorola (no hace mucho adquirida por Google), conocido por su trabajo con WebGL, sobre todo por algunas demos de carga de ficheros, ya sean de modelos independientes o escenarios completos. Empecé a utilizar la librería por la inercia de haberme basado en código copiado directamente de learningwebgl.com, que es la versión para WebGL de los clásicos tutoriales de NeHe para OpenGL. Y si bien me pareció una buena idea al principio, ahora ya no lo tengo tan claro. Para realizar un par de operaciones con matrices, que es lo único que se necesita habitualmente para renderizar algo por pantalla, pues la verdad es que casi cualquier cosa sirve, incluida gl-matrix. Pero cuando se trata de realizar algo más elaborado enseguida se tropieza uno con ciertos aspectos de diseño de la librería que resultan bastante molestos. La motivación principal de la librería es permitir trabajar con vectores, matrices o quaterniones utilizando el tipo Array clásico de JavaScript, o el más reciente ArrayBuffer si está disponible en el navegador, e incluso mezclarlos indistintivamente. Y sobre todo poder acceder a los elementos individuales de los arrays utilizando la notación clásica de corchetes con el propósito de aumentar el rendimiento al eliminar cualquier tipo de acceso intermedio y ser lo más transparente posible en su uso. El inconveniente es que para conseguir sus propósitos obliga a pagar un precio un tanto alto. Veamos un código de ejemplo clásico para sumar dos vectores: vec3.add(a, b, c);Lo que uno espera es El inconveniente de esta solución es que dificulta la lectura del código si no se conoce la librería, resulta muy poco intuitivo. E incluso después de haber trabajado con ella es complicado, ya que también se dice en la documentación que el último parámetro de la función es opcional, por lo que el siguiente código es válido: vec3.add(a, b);Lo que uno espera ahora es Incluso es posible escribir el mismo código de una tercera manera: vec3.add(a, b, a);Lo que uno espera ahora intuitivamente es Un problema más derivado de este diseño puede verse en el siguiente código donde se intenta concatenar dos operaciones: vec3.add( vec3.add(a, b), c);Lo que uno espera es vec3.add( vec3.add(a, b, d), c);Pero realmente esa nueva variable es un artificio que la librería nos obliga a introducir, a nuestro programa no le aporta nada, y además dificulta la escritura de operaciones concatenadas como se observa en el siguiente código donde se han añadido la declaración de las variables necesarias: var a = vec3.create(),Lo que uno espera es que el código anterior funcione, pero lo que ocurre ahora es que se produce un error en tiempo de ejecución. Y es que la librería espera además que la variable que almacena el resultado intermedio le llegue inicializada desde el cliente igual que el resto: ...La clave está en que la librería evita crear cualquier tipo de objeto intermedio con el propósito de optimizar su rendimiento, delegando en el cliente la responsabilidad de instanciar la cantidad mínima necesaria de objetos intermedios que realmente necesite para el cálculo que pretende realizar. El problema es que a pesar de esto no consigue su objetivo, y no resulta más optima que otras librerías equivalentes según los tests de rendimiento. Por otra parte, a estas alturas supongo que más de uno ya se habrá percatado por la nomenclatura de que la librería no crea ningún tipo de objeto a la manera clásica, con sus atributos y métodos, sino que más bien todos los métodos son expuestos públicamente de manera "estática", lo que obliga a escribir una y otra vez el tipo de los datos con los que se trabaja ( Esta última característica concreta del diseño lo utilizan bastantes librerías, pero tiene dos inconvenientes. El primero es que obliga a saber el tipo de la variable con la que se está trabajando en todo momento, en vez favorecer la abstracción y centrarse en la operación en vez del tipo de los operadores. Y el segundo es que si desea cambiar de tipo, para pasar de tres a cuatro dimensiones por ejemplo, entonces hay que realizar la sustitución en todo el código en vez de tan sólo en la declaración. ¡Demasiadas cosas en las que pensar para hacer una simple suma de vectores!
Juan Mellado, 14 Febrero, 2012 - 13:32
Un vídeo de Hangar en acción. Demo online:
Juan Mellado, 13 Febrero, 2012 - 18:05
Hoy he liberado los fuentes del último proyecto en JavaScript en el que he estado trabajando. Es un visor de modelos 3D almacenados en formato AC3D (.ac) que utiliza WebGL para renderizar. He estado utilizando modelos de aviones de FlightGear para probarlo, así que he decidido llamarlo Hangar. He creado una pequeña página web con cuatro modelos escogidos de prueba. Tiene implementado un controlador a modo de trackball que permite rotar y ampliar los modelos utilizando el botón izquierda y la rueda del ratón. Necesita un navegador moderno como las últimas versiones de Chrome o Firefox para funcionar (Y no, IE 9 no es un navegador moderno) ![]() El formato AC3D lo utilizan algunos programas como FlightGear, un simulador aéreo de código abierto, o Torcs, un simulador de conducción deportiva también de código abierto. Es un formato de texto plano que originalmente creó un programa de modelado 3D llamado, lógicamente, AC3D. Mi idea era hacer algo sencillo, pero al final he acabado incorporando un montón de esas pequeñas características que pasan desapercibidas cuando funcionan bien. AC3D Los objetos están definidos de una manera jerárquica, de forma que un objeto puede tener hijos. Además, cada objeto tiene una matriz de rotación y un vector de rotación que aplica a sí mismo y a todos su hijos, por lo que hay que ir calculando la transformación resultante a medida que se baja por el árbol de descendencias. Teselación Me he encontrado con modelos de todo tipo y definidos de casi cualquier forma. Al final he decidido ignorar los polígonos degenerados. Es decir, polígonos con menos de tres vértices, polígonos con coordenadas colineales, polígonos con vértices no contenidos dentro de un único plano, polígonos con aristas que se cruzan, ... Normales La normal a un vértice concreto se calcula como la suma de todas las normales de todas las caras que comparten dicho vértice. Aunque a este respecto el fichero incorpora un parámetro por objeto llamado "crease". Este parámetro es un ángulo, y sirve para generar lo que habitualmente se conocen como bordes duros (hard edges). Si el ángulo que forman la normal de una cara y su vecina supera dicho parámetro entonces esa normal vecina no se tiene en cuenta para calcular la normal en el vértice compartido. SGI Mi implementación soporta .sgi, .rgba, .rgb, .ra y .bw. Es decir, todas las combinaciones posibles, aunque he ignorado deliberadamente algunas características de la especificación, como la posibilidad de usar paletas de colores o escoger entre 8 ó 16 bits por canal. En la práctica todas las imágenes acaban siendo de cuatro canales y con 8 bits por canal en el clásico formato RGBA. Loader Muy útil. Renderer El formato AC3D permite incluir luces en los modelos, pero las he ignorado a la hora de renderizar, utilizando en cambio una luz fija estática direccional sin factores de atenuación. Mi idea era representar modelos de objetos independientes, no escenas completas, por lo que no he encontrado sentido en incluirlas. Aparte de que acabado no fiándome de los modelos, con materiales un tanto extraños a mi parecer. Para minimizar los cambios de estado de la tarjeta gráfica he agrupado todos los polígonos por programa, material, tipo, y todo lo que se me ha ocurrido. Por ello, a diferencia de otros proyectos anteriores, he decidido utilizar Transparencias Lo que me duele es que se supone, repito "se supone", que los materiales incluyen un atributo para indicar si tienen algún tipo de transparencia. Desgraciadamente esto no ocurre así en la práctica. Al final cuando algún modelo no se visualizaba correctamente, abría el fichero, localizaba el material y lo cambiaba a mano. Esto es algo que en general creo que tendría que revisar con el objetivo de entender la manera en que interpretan los materiales programas como FligthGear o Torcs aprovechándome del hecho de que son de código abierto. Autofit De esto también me gustaría escribir un post individual en el futuro, ya que he encontrado distintas soluciones en Internet y no me he quedado del todo satisfecho con la que yo mismo he implementado. TrackBall Resumiendo Una aproximación más práctica al problema hubiera sido utilizar un motor 3D ya existente, como el popular Three.js, y limitarme a hacer un conversor del formato .ac a algún formato que acepte el motor. Pero claro, ¡me hubiera perdido la parte más divertida! |