Lego Models (LDraw) Loaded from Java

Back in 2007 I step on LDraw and MLCAD WEB sites. For someone like me, who played a lot with Lego during his childhood, LDraw and MLCAD are/were certainly an incommensurable source of joy.

I found those WEB sites just before Christmas vacation so I raged to build in MLCAD many of the Lego models I had when I was a child.

By then I was also doing some small work on 3D graphics programming, using mostly Java3D and JOGL, so I developed a small piece of software intended to load LDraw models from Java. The following is a video of that software, showing some of the Lego models I build that Christmas:

Recently, I got a request to write a video explaining how to run the software (I have to admit that there isn’t a tutorial, and it’s not exactly easy to run it). So here goes the tutorial.

First, import the code from the SVN repository (I will not detail this, you’ll have to figure it out). The source code can be found here:

http://code.google.com/p/foo-org-ve/

You will need to get the Lego project. I use Eclipse, so the project is already configured to run in Eclipse.

After importing the project, you should try to run the class view.Main. It will probably not run at first, so you will find in front of this exception:

java.lang.NullPointerException
  at view.Main.display(Main.java:112)
  at com.sun.opengl.impl.GLDrawableHelper.display(GLDrawableHelper.java:78)
  at javax.media.opengl.GLCanvas$DisplayAction.run(GLCanvas.java:281)
  at com.sun.opengl.impl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:194)
  at javax.media.opengl.GLCanvas$DisplayOnEventDispatchThreadAction.run(GLCanvas.java:298)
  at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:199)
  at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
  at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
  at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
  at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
  at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

This is because you don’t have the ldraw parts and the models in the right place. To solve it you have to uncompress the files ldraw.tar.gz and model.tar.gz located in the root of the project.

That way you will end with two additional directories in project root: ldraw and model.

Go back to eclipse and refresh the project: select the project Lego and hit F5, or select the project and ”right click->refresh”.

Then go again to view.Main and change the part:

// ***************************************
// XXX: Change according the real location
private static final String ldraw_base = "/home/dmi/workspace/Lego/ldraw"; // LDraw home
private static final String model_base = "/home/dmi/workspace/Lego/model"; // Where to the models to be displayed
// ***************************************

Set the path “/home/dmi/workspace/Lego/XXX” in both entries according to the location of the project root. If you just uncompressed the ldraw and model gz files in the project root left the last part unchanged, if you put the files anywhere else change the route accordingly.

Then, in the same file, go to the part with the models (I only show a few, but you should see a lot of commented similar lines:

part = partLoader.loadPart("1499-1.mpd");  // Medium twin seat spaceship
//part = partLoader.loadPart("885-1.mpd");   // Very small spaceship (5)
//part = partLoader.loadPart("487-1.mpd");   // Medium Ship with deployable car
//part = partLoader.loadPart("6804-1.mpd");  // Very small car (2)
//part = partLoader.loadPart("6807-1.mpd");  // Very (very) small spaceship with robot

And uncomment one line (you can uncomment as many as you want, but it should display the last one). If every thing goes well you should see something like:

Lego_th

You can add your own models in the model directory and then add the appropriate line there to load it.

If you run it and find this exception:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no jogl_drihack in java.library.path
  at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1734)
  at java.lang.Runtime.loadLibrary0(Runtime.java:823)
  at java.lang.System.loadLibrary(System.java:1028)
  at com.sun.opengl.impl.NativeLibLoader$DefaultAction.loadLibrary(NativeLibLoader.java:78)
  at com.sun.opengl.impl.NativeLibLoader.loadLibrary(NativeLibLoader.java:101)
  at com.sun.opengl.impl.NativeLibLoader.access$100(NativeLibLoader.java:47)
  at com.sun.opengl.impl.NativeLibLoader$3.run(NativeLibLoader.java:141)
  at java.security.AccessController.doPrivileged(Native Method)
  at com.sun.opengl.impl.NativeLibLoader.loadDRIHack(NativeLibLoader.java:139)
  at com.sun.opengl.impl.x11.DRIHack.begin(DRIHack.java:105)
  at com.sun.opengl.impl.x11.X11GLDrawableFactory.(X11GLDrawableFactory.java:66)
  at java.lang.Class.forName0(Native Method)
  at java.lang.Class.forName(Class.java:169)
  at javax.media.opengl.GLDrawableFactory.getFactory(GLDrawableFactory.java:111)
  at javax.media.opengl.GLCanvas.(GLCanvas.java:113)
  at javax.media.opengl.GLCanvas.(GLCanvas.java:82)
  at javax.media.opengl.GLCanvas.(GLCanvas.java:75)
  at view.Main.main(Main.java:240)

It’s because java is not finding JOGL binary libraries. Here you have two options:

  • Add the libraries in a place where they can be found by default. For example, in Linux add them in /usr/lib (or /usr/local/lib) and then run ldconfig. In windows add them in “C:\windows\system32” (I’ve not been windows user for a long time, so find yourself the right place if it does not work). Note that this option will require root access in Linux.
  • Another option is to go in eclipse to the “Run Configurations” and manually set up the library path. To go to “Run Configurations”, pull the run button (don’t click the button, just the tiny arrow besides the button) -> Run Configurations” -> Main”. Main is the name of the “view.Main” executable class. Then go to Arguments -> VM Arguments, and add:
-Djava.library.path=/home/dmi/workspace/Lego

Change “/home/dmi/workspace/Lego” according to your set up.

RunConfiguration1

RunConfiguration2_th

The JOGL binary libraries are included in the root of the project, the files are:

-rw-r--r-- 1 dmi dmi   20480 2012-05-20 16:51 jogl_awt.dll
-rw-r--r-- 1 dmi dmi  114688 2012-05-20 16:52 jogl_cg.dll
-rw-r--r-- 1 dmi dmi  290816 2012-05-20 16:51 jogl.dll
-rw-r--r-- 1 dmi dmi   10107 2012-05-20 16:51 libjogl_awt.so
-rw-r--r-- 1 dmi dmi  185584 2012-05-20 16:51 libjogl_cg.so
-rw-r--r-- 1 dmi dmi    6421 2012-05-20 16:51 libjogl_drihack.so
-rw-r--r-- 1 dmi dmi  962937 2012-05-20 16:51 libjogl.so

The *.so files are for Linux, the *.dll files are for Windows.

If you are using any other IDE instead of Eclipse, or you want to run from the console, or make a jar, etc, you will have to figure it our by yourself, but given this tutorial it should not be difficult. For example, I can run it directly from the console with the commands:

$ > export LD_LIBRARY_PATH=/home/dmi/workspace/Lego/
$ > java -cp bin/:lib/jogl.jar:lib/commons-logging-1.1.jar:lib/commons-lang-2.1.jar view.Main

I wrote the code in 2007 and even then I was using an old version of JOGL (Don’t ask why). A good contribution would be to migrate it to use a newer version of JOGL. Also, considering what I’ve learn since then about software and 3D graphics, I think the code could be probably improved a lot.

Also there is a known bug with transparency. If you use transparent Lego pieces, it’s possible that something behind them would not display correctly. This is because transparency (using depth buffer) requires to draw all transparent geometry after drawing non transparent geometry (on top of non transparent geometry). Currently the code is not doing this, so if any object behind a transparent window is drawn after the window, it will just not been drawn, because it will be behind the window and depth buffer will prevent it from being drawn. One solution would be to detect the transparent objects and add them to a list instead of drawing them, and at the end, draw them after all the non transparent objects.

Relating to the license of the code, basically you can do whatever you want with it. I only ask two things: 1) If you use it, change it, blog about it, or do anything else with it, please mention me as the original author and link back to this post. 2) If you make any changes, improvements, convert it to a library, etc, I would like to know it and have the possibility to add those changes to the original project.

UPDATE

Kevin Loddewykx has contributed a great update to the project. I’ll let his changelog speak for itself:

Update for project “Lego”
1) Uses the most recent version of jogl + gleugen and log4j 1.2
2) Uses the most recent version of ldraw parts library and ldconfig.ldr

ENHANCEMENTS :
3) Camera rotates now instead of the object
4) Model can be given through the command line
5) Pressing R will now change the model when it is changed in the code, when you
are real-time debugging

BUG FIXES :
6) Transparency has been updated, so transparent objects always will be rendered
last
7) Previous when more then one model was uncommented, it’s was possible that
models would get mixed up.

MISC :
8) Extra model added train.ldr :)

The transparency fix is especially important, the following picture shows the bug:

screen_lego_wrong

And the following picture shows the same model after Kevin’s fix:

screen_lego_OK

Thanks a lot Kevin for the update, it works like a charm :)

Compañías y Productos RAIS en el A2011 (conclusión)

Hace algún tiempo escribí sobre los productos que se iban a desarrollar en las asignaturas de Ingeniería de Software y Bases de Datos durante el semestre A2011 utilizando la estrategia RAIS. El A2011 terminó y los productos se desarrollaron (o casi). Me gustaría compartir los resultados, que en general, fueron bastante buenos.

MagicRoot

MagicRoot es un juego de cartas multi-jugador, y también, es el nombre de la compañía RAIS que lo desarrolló. El objetivo del juego es vencer al oponente utilizando cartas mágicas que representan los distintos elementos: Aire, Agua, Fuego y Tierra. La idea está basada en Triple Triad que es un mini-juego de Final Fantasy VIII (Espero que no nos demanden ;-).

El juego fue implementado en Java usando una arquitectura cliente-servidor. La interfaz de usuario está implementada en Swing. El servidor y el esquema de comunicación cliente-servidor implementan una arquitectura muy desacoplada y estructurada, que incluye el uso de varios patrones de diseño y que gestiona la lógica y los estados del juego utilizando una máquina de estados implementada de forma explícita. Sobre éste último punto tengo mucho que decir, e inclusive hay un trabajo de tesis en curso, pero eso es parte de otra historia. La capa de persistencia está implementada con Hibernate como ORM y MySQL como base de datos.

El código fuente de la aplicación se puede encontrar en:

http://code.google.com/p/magicroot

Sobre MagicRoot y su producto hay que decir dos cosas:

1.- La propuesta inicial del juego no me gustaba (debo admitirlo). Aquí, como Jefe Ejecutivo RAIS, es necesario tener fe en la gente con la que se trabaja. Lo que tenía en frente al principio del semestre eran cuatro “Ingenieros RAIS”, muy motivados con una idea clara entre manos. Esa motivación para mi fue suficiente para decidir que el producto era viable, aún cuando en lo personal no estaba completamente seguro.

2.- La propuesta inicial era técnicamente compleja. He desarrollado este tipo de aplicaciones, dado consultorías al respecto y conozco la complejidad del problema: Cliente gráfico, múltiples hilos del lado del cliente y del servidor, comunicación por sockets, protocolo de comunicación, lógica y reglas del juego complejas, máquinas de estado, Hibernate, etc. Esta complejidad añade algo de riesgo, y he visto más de un equipo de desarrollo empantanarse en tales condiciones. Esto me preocupaba mucho, tomando en cuenta que no conocía bien las capacidades técnicas o la madurez del grupo de programadores que tenía en frente.

A algunos desarrolladores de software experimentados el proyecto quizá les pueda parecer algo trivial, pero tomen en cuenta que el trabajo se debe hacer en 4 meses (o menos) a tiempo parcial y que estamos hablando de estudiantes del 7mo semestre que nunca han programado en Java y mucho menos conocen muchas de las tecnologías utilizadas.

Desde el punto de vista de RAIS, en el caso de los cursos de IS y BD, una estrategia que ha funcionado para manejar la complejidad de un producto es que al inicio del curso todos los integrantes de una compañía conozcan el nivel de dificultad y los riesgos presentes en el producto al que se enfrentan.

Me tomo muy en serio esto, explico los riesgos, el volumen de trabajo, los retos y capacidades técnicas necesarias, y me aseguro de que todos los integrantes de la compañía sepan en qué problema se están metiendo. Si hay algún aspecto del producto que desconozco (cosa que sucede con cierta frecuencia) lo digo directamente y sin rodeos, es decir, puedo estimar, hacer comentarios, tratar de adivinar, aconsejar, etcétera, pero soy muy claro de que se trata de un riesgo y no doy falsa confianza sobre algo que en el fondo no conozco y se sale de mi control.

Luego dejo que tomen la decisión por ellos mismos, y si desean cambiar de producto, no hay problema. Esto último también se deja bien claro, y es parte de mi visión particular con RAIS, yo como Jefe Ejecutivo puedo aconsejar y ayudar, pero las decisiones finalmente las toman las compañías de forma voluntaria.

Afortunadamente, los ingenieros de MagicRoot decidieron seguir adelante con el producto, aún después de conocer la complejidad y las dificultades a las que se enfrentaban. Debo decir que esa fue una verdadera demostración de coraje que siempre admiraré.

El resultado no sólo cumplió con las expectativas que tenía, sino que las superó. En verdad MagicRoot fue un producto espectacular, con una arquitectura muy sólida, muy fácil de mantener y manejar, que les permitía arreglar bugs y hacer cambios y ajustes en las reglas del juego muy fácilmente.

Como nota curiosa, los integrantes de MagicRoot cursaron Inteligencia Artificial el semestre siguiente e implementaron la inteligencia del juego, es decir, hicieron que fuese posible también jugar contra la computadora y no sólo contra la otra persona.

El equipo de MagicRoot está compuesto por:

Rafael Solórzano (Gerente), César Vielma, Andres Zamora y Jesus Daniel Garcia.

Mentes en Guerra

Mentes en Guerra, desarrollado por CodeSolids, es un juego de batalla multi-jugador en el que los jugadores pueden crear personajes, combatir con otros personajes de otros jugadores, ganar puntos, aumentar de nivel y comprar equipamiento para mejorar a su personaje.

El juego es una aplicación WEB desarrollada en Java, utilizando Echo3 para la interfaz de usuario, Hibernate como ORM y MySQL como base de datos.

Esta compañía llevó el uso de Echo3 al extremo, y hay cosas que, desde el punto de vista visual si bien imagino como están hechas, en el fondo no tengo una idea clara de como las implementaron. En especial resulta interesante ver la parte de la batalla, en la que los personajes efectivamente tienen animaciones, se mueven y atacan a los otros personajes.

El código fuente de la aplicación se puede encontrar en:

http://code.google.com/p/codesolids-project

Es interesante mencionar que Mentes en Guerra fue uno de los últimos productos que se desarrollaron bajo el concepto de aplicación WEB implementada con Echo3, ya que, pareciera que después de MagicRoot, todas las compañías de los cursos siguientes han querido desarrollar únicamente aplicaciones en Swing.

En general, CodeSolids fue un grupo bueno, la compañía no tuvo ningún problema serio a lo largo del semestre y avanzó bastante bien. Quizá cerca del final sentí cierta preocupación y cierto retraso en el desarrollo del producto, pero finalmente todo salió bien, entregaron un buen producto, que funciona y satisface (mucho) las expectativas.

El equipo de CodeSolids está compuesto por:

José Luis Pérez (Gerente), Antonio Lopez, Karla Moreno, Fernando Osuna y Hector Prada.

Greed Treasure

No todo salió completamente bien en el A2011, de las tres compañías, una de ellas, Thinking & Looking, fracasó rotunda y estrepitosamente.

El fracaso de Thinking & Looking fue doloroso. ¿La razón? El producto que decidieron desarrollar me importaba mucho, era una idea original que realmente quería ver funcionando, al menos a nivel de prototipo o prueba de concepto.

¿Por qué fracasó la compañía? Tengo una teoría (bueno, quizá varias teorías a decir verdad), pero aún así es difícil estar completamente seguro. En mi opinión los factores más importantes fueron:

1.- Falta de liderazgo: El fracaso está asegurado sin un liderazgo fuerte, que logre darle coherencia y sentido a la compañía (y al producto), y sobre todo, que logre dar buenos ejemplos de trabajo al resto del grupo. En este caso, en mi opinión, el gerente no supo aprovechar el equipo de trabajo del cual formaba parte, no supo motivar al equipo y lograr que trabajaran de forma conjunta.

2.- Falta de compromiso de los ingenieros con la compañía y el producto: Esta es la otra cara de la moneda del punto anterior. Creo que hubo mucha, pero muchísima, falta de compromiso. Un buen liderazgo debería crear ese compromiso, pero no toda la responsabilidad debe pesar sobre el gerente de la compañía.

Si el gerente no puede sacar a flote a su grupo, alguien dentro de la compañía debería asumir esa responsabilidad y lograr que el equipo salga adelante, esto ha ocurrido en otros casos y en otras compañías. Sin embargo esto no sucedió en Thinking & Looking. Todos los ingenieros simplemente se limitaron a hacer lo mínimo que se les pedía y no pudieron salir adelante, no hubo un liderazgo emergente, y de hecho, fue bastante triste verlos al final del semestre buscando la forma de culparse mutuamente.

3.- Problemas de comunicación, coordinación, colaboración y trabajo en equipo: Thinking & Looking tenían el balance adecuado de destrezas y capacidades técnicas, pero en mi opinión simplemente no supieron aprovechar esta fortaleza. Es decir, reunieron un conjunto interesante de talentos individuales, pero nunca lograr en verdad “formar” una compañía. El punto anterior también forma parte de esto último.

4.- No se puede culpar del fracaso al producto: El producto tenía requisitos muy claros y bien definidos, no era excesivamente complejo y no tenía riesgos técnicos serios. El riesgo técnico más fuerte, que era dibujar e interactuar con un mapa estaba ya controlado, a nivel de prototipo y de trabajos hechos en el semestre anterior.

No voy a mencionar los nombres de los integrantes de Thinking & Looking, en mi opinión ya bastante problemas tuvieron tratando de sobrevivir al A2011, de modo que les voy a dejar en el anonimato.

De los tres productos del A2011, dos fueron en mi opinión un éxito rotundo. Aún con el doloroso fracaso de Thinking & Looking creo que fue un buen semestre en el que se obtuvieron excelentes resultados. Inclusive puedo decir que aprendí mucho del fracaso de Thinking & Looking, y que espero ahora poder detectar e intervenir esas “compañías patológicas” con el tiempo suficiente como para poder resolver el problema, e inclusive, desarrollar políticas de organización de compañías que eviten que este tipo de desastres vuelvan a ocurrir.

¡Hasta la próxima experiencia RAIS! :-)

¿Qué se puede mejorar en estas pantallas de Gmail?

Hoy me topé con lo que en mi opinión es una pequeña falla de usabilidad en GMail. Es curioso ver que hasta Google se equivoca de tanto en tanto en cuestiones de usabilidad.

Aquí les dejo el acertijo (al menos para mis estudiantes de Ingeniería de Software). Los voy a dejar mirar las pantallas por un rato a ver si pueden descubrir el problema. Luego, en unos días, escribo mi opinión.

La primera pantalla (click para agrandar):

gmail1_th

Muestra la interfaz que se ve luego de que se hace click en “compose” para escribir un nuevo correo y justo antes de enviar el correo. Es decir, click en “compose”, se escribe el nuevo correo y luego se tendría que hacer click en “send” para enviar el correo.

La segunda pantalla:

gmail2_th

Muestra lo que aparece al abrir un correo y hacer click en “reply” para contestar. Es decir, se selecciona un correo del inbox, se hace click en “reply”, se escribe la respuesta y luego se tendría que hacer click en “send” para enviar el correo.

Puede que entre ambas pantallas exista más de un problema, pero en mi opinión hay uno que muy contundente que se puede argumentar de forma muy sólida. De hecho, este problema ya me ha hecho equivocarme al menos un par de veces, por suerte, sin consecuencias graves.

¿Puede usted, estimado lector, encontrar y argumentar el problema?

Como ya dije, voy a dejar pensar un rato a mis estudiantes de IS, y dependiendo de lo que pase, en unos días edito este post (o publico otro) describiendo el problema.

Por cierto, ayuda tratar de abrir las pantallas en GMail, no sólo ver las imágenes anteriores.