TheJach.com

Jach's personal blog

(Largely containing a mind-dump to myselves: past, present, and future)
Current favorite quote: "Supposedly smart people are weirdly ignorant of Bayes' Rule." William B Vogt, 2010

Getting started with jMonkeyEngine and Clojure

"I want to write a 3D game in Clojure! But I don't want to bother with raw OpenGL crap, I had my fill of that with Python and PyOpenGL... And I didn't particularly like Panda3D for Python either. I guess I won't be satisfied until I make my own engine one day. But in the meantime, what's a good engine for Java? I remember hearing years ago about something with monkeys..."

That was my thought process a couple weeks ago, and a swift google reminded me of jMonkeyEngine, or JME for short. I had first heard about it back in 2007 or so, but had never played with it seriously. So I figured, why not? Unfortunately I was under some misconceptions that made the introductory period harder. I'll go over the biggest one quickly. JME is better thought of as a collection of libraries rather than a single jar you dump in your project and start building off of. On top of this collection, JME includes a nice IDE and they really want you to use it all together as part of an SDK platform. I really want to use just vim and the command line and treat their stuff as a library. So immediately I'm at odds with the tool, but no matter, it's not impossible to overcome.

Anyway, let's get started, step-by-step. Create your new clojure project with the lein tool:


$ lein new jme_clj
Created new project in: /home/kevin/jme_clj
Look over project.clj and start coding in jme_clj/core.clj


Now to download a nightly zip from JME's site (don't get the giant SDK, just the collection of libraries). Extract the lib folder into your project's folder. Let's test out importing something!


$ lein repl
Copying 1 file to /home/kevin/jme_clj/lib
REPL started; server listening on localhost port 45085
user=> (import com.jme3.app.SimpleApplication)
ClassNotFoundException com.jme3.app.SimpleApplication java.net.URLClassLoader$1.run (URLClassLoader.java:202)
user=>


Hmm, that's no good. What'd it say about copying a file to lib? Let's look in the directory... Heeey, where'd all the other jar files go?

Lein uses maven in the backend, and if your deps have changed at all (or it's a fresh project) and you run the repl it will re-acquire the deps after first wiping your lib dir. But after that, if you don't call lein deps directly, you'll be fine storing local jars in there. For a more permanent solution, you have to setup a local maven repository to keep your local jars around in.

Also I noticed that lein gave me Clojure 1.3 by default. Clojure 1.4 is out, yo! So I changed my project file and ran lein deps myself, then I re-extracted the JME lib jars. Let's try this again.


$ ls lib
clojure-1.4.0.jar jME3-jogg.jar j-ogg-vorbisd.jar
eventbus.jar jME3-lwjgl.jar lwjgl.jar
jbullet.jar jME3-lwjgl-natives.jar nifty-default-controls.jar
jinput.jar jME3-networking.jar nifty-examples.jar
jME3-blender.jar jME3-niftygui.jar nifty.jar
jME3-core.jar jME3-plugins.jar nifty-style-black.jar
jME3-desktop.jar jME3-terrain.jar stack-alloc.jar
jME3-effects.jar jME3-testdata.jar vecmath.jar
jME3-jbullet.jar j-ogg-oggd.jar xmlpull-xpp3.jar
$ lein repl
REPL started; server listening on localhost port 62280
user=> (import com.jme3.app.SimpleApplication)
com.jme3.app.SimpleApplication
user=>


Yay! Now we can start going through the beginner's tutorial. Maybe.

Let's check out the Java source code that we'll need to convert to Clojure:


package jme3test.helloworld;

import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.math.ColorRGBA;

/** Sample 1 - how to get started with the most simple JME 3 application.
* Display a blue 3D cube and view from all sides by
* moving the mouse and pressing the WASD keys. */
public class HelloJME3 extends SimpleApplication {

public static void main(String[] args){
HelloJME3 app = new HelloJME3();
app.start(); // start the game
}

@Override
public void simpleInitApp() {
Box b = new Box(Vector3f.ZERO, 1, 1, 1); // create cube shape at the origin
Geometry geom = new Geometry("Box", b); // create cube geometry from the shape
Material mat = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md"); // create a simple material
mat.setColor("Color", ColorRGBA.Blue); // set color of material to blue
geom.setMaterial(mat); // set the cube's material
rootNode.attachChild(geom); // make the cube appear in the scene
}
}


Looks simple enough, though there are two questions I'd like answered. What is assetManager, is it an object member for SimpleApplication objects? It doesn't sound like one per se... What is rootNode, maybe it's an object member? Seems more likely. The tutorial doesn't say for either... Well, let's assume they're both finalized object members and do the direct java-to-clojure transformation. If the assumption is wrong we'll fix it later. This will be in your core.clj file.


(ns jme_clj.core
(:import [com.jme3 app.SimpleApplication
material.Material
scene.Geometry
scene.shape.Box
math.Vector3f
math.ColorRGBA]))

(def app (proxy [SimpleApplication] []
(simpleInitApp []
(let [b (Box. Vector3f/ZERO 1 1 1)
geom (Geometry. "Box" b)
mat (Material. (.assetManager this)
"Common/MatDefs/Misc/Unshaded.j3md")]
(.setColor mat "Color" ColorRGBA/Blue)
(.setMaterial geom mat)
(doto (.rootNode this) (.attachChild geom))))))

(defn -main [& args]
(.start app))


And try running it with lein run, oops we forgot to add the :main jme_clj.core to our project.clj file after the description. Fix that, try again, and...

Hey, the JME Launcher thing popped up. That's encouraging. Running it with defaults... and crash. With a lot of output. Looks like the error was this:

java.lang.IllegalArgumentException: No matching field found: assetManager for class jme_clj.core.proxy$com.jme3.app.SimpleApplication$0


Okay, so assetManager isn't a final object member. Then what the heck is it? In the nightly you downloaded there was also a source folder, let's look in that and at Application.java. We see both applicationManager and rootNode as protected members that are non-final, and later on we see two getter methods on them. Perfect. So update the code with the replacements using the getter methods:


...
(.getAssetManager this)
...
(.getRootNode this)
...


And run again, and hey, it works. Tutorial 1 complete!

Before I bothered reading the Java source I googled around and found someone else has already played with JME and Clojure, too. They also set up an assetManager that is independent from the actual application (as I thought it would have been by default), and some default app settings that make the pop-up before launch go away. Check them out, they've got some cool stuff. Anyway, I integrated (read: stole) their configuration and so now the finished "Hello World" for JME looks like this:


(ns jme_clj.core
(:import [com.jme3 app.SimpleApplication
system.AppSettings
system.JmeSystem
material.Material
scene.Geometry
scene.shape.Box
math.Vector3f
math.ColorRGBA]))

(def desktop-cfg (.getResource (.getContextClassLoader (Thread/currentThread))
"com/jme3/asset/Desktop.cfg"))

(def assetManager (JmeSystem/newAssetManager desktop-cfg))

(def ^:dynamic *app-settings* (doto (AppSettings. true)
(.setFullscreen false)
(.setTitle "jme_clj")))

(def app (proxy [SimpleApplication] []
(simpleInitApp []
(org.lwjgl.input.Mouse/setGrabbed false)
(let [b (Box. Vector3f/ZERO 1 1 1)
geom (Geometry. "Box" b)
mat (Material. assetManager
"Common/MatDefs/Misc/Unshaded.j3md")]
(.setColor mat "Color" ColorRGBA/Blue)
(.setMaterial geom mat)
(doto (.getRootNode this) (.attachChild geom))))))

(defn -main [& args]
(doto app
(.setShowSettings false)
(.setPauseOnLostFocus false)
(.setSettings *app-settings*)
(.start)))


There's a lot of other nifty stuff they did, including the standard Clojure idiom of suppressing exceptions. Perhaps I'll have to integrate some of it later on, too...


Posted on 2012-05-13 by Jach

Tags: clojure, jMonkeyEngine, programming

Permalink: http://www.thejach.com/view/id/251

Trackback URL: http://www.thejach.com/view/2012/5/getting_started_with_jmonkeyengine_and_clojure

Back to the top

Back to the first comment

Comment using the form below

(Only if you want to be notified of further responses, never displayed.)

Your Comment:

LaTeX allowed in comments, use $$\$\$...\$\$$$ to wrap inline and $$[math]...[/math]$$ to wrap blocks.