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 :)