M

How To Use LWJGL From Clojure

February 16, 2013 — 3 minutes long

I recently decided to try my hand at 3D programming in Clojure, and I settled on trying out LWGJL. Unfortunately, accessing its API takes a bit more than adding dependencies to project.clj, and after an hour of fiddling and searching I wanted to share my solution so that others might benefit.

TL;DR

  1. Add lwjgl to project.clj.
  2. Download the stable release of LWJGL and copy the files from native/<my-OS> into native/ within your project.
  3. Add native/ to the JVM’s library path.

First, The LWJGL Library

The first step, actually, is to update project.clj with lwjgl from Central. First we need to put [org.lwjgl.lwjgl/lwjgl "2.8.5"] inside :dependencies. If you’re starting a new Leiningen project, project.clj might look something like this:

(defproject my-project "0.1.0-SNAPSHOT"
  ; ...
  :dependencies [[org.clojure/clojure "1.4.0"]
                 [org.lwjgl.lwjgl/lwjgl "2.8.5"]])

You’ll probably want to find out what the current stable version is, and then replace "2.8.5" with that.

Adding this satisfies the library portion of LWJGL, but if we were to stop here, we would probably end up with an error like UnsatisfiedLinkError no lwjgl in java.library.path when trying to import LWJGL classes from Clojure.

Second, Native Extensions

LWJGL comes with several native extensions for Mac, Linux, and Windows, and it needs those available when running on the respective OS. For some reason, those files do not come with the download from Central and need to be “installed” by hand.

First we’ll create a directory at the top level of our project to hold the extensions. We can name it native/, or whatever. The only important thing is that we remember that name so we can tell java about it in a moment.

Go to the LWJGL downloads page and download the latest stable zip file. Extract the contents and copy every file within the native/<my-OS> directory into our project’s native/ directory. It’s important that the files are directly under native/ and not in subdirectories, because java won’t look inside those.

Third, Changing The JVM’s Library Path

Leiningen’s project.clj file allows for a :jvm-opts key-pair that that is used to set java’s command-line flags. Here we need to tell the JVM to look inside the directory where our native extensions are located. In my case, I used native/, so you can see that I told it about that. I also used Clojure’s str to concatenate my custom library path to the beginning of the existing path. The new project.clj looks something like this:

(defproject my-project "0.1.0-SNAPSHOT"
  ; ...
  :dependencies [[org.clojure/clojure "1.4.0"]
                 [org.lwjgl.lwjgl/lwjgl "2.8.5"]])
  :jvm-opts [~(str "-Djava.library.path=native/:"
                   (System/getProperty "java.library.path"))])

Once this is completed, you should be able to import away without issue.