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

Clojure and jMonkeyEngine Tutorial 3

I apologize for not continuing these in a timely manner, a lot has been going on. Update: 4 and 5 completed.

Here's a link to the official tutorial 3.

Here's the translated Clojure code:

(ns jme_clj.core
(:import [com.jme3 app.SimpleApplication


(def desktop-cfg (.getResource (.getContextClassLoader (Thread/currentThread))

(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 [; define assets
; teapot in space
teapot (.loadModel assetManager "Models/Teapot/Teapot.obj")
mat_default (Material. assetManager "Common/MatDefs/Misc/ShowNormals.j3md")

; wall with texture
box (Box. (Vector3f/ZERO) 2.5 2.5 1.0)
wall (Geometry. "Box" box)
mat_brick (Material. assetManager "Common/MatDefs/Misc/Unshaded.j3md")
wall-pos '(2 -2.5 0)

; line of text
gui-font (.loadFont assetManager "Interface/Fonts/Default.fnt")
txt (BitmapText. gui-font false)

; ninja model from OgreXML
ninja (.loadModel assetManager "Models/Ninja/Ninja.mesh.xml")
ninja-pos '(0 -5 -2)
; this model needs lighting
sun (DirectionalLight.)
; modify and attach assets
(.setMaterial teapot mat_default)
(.attachChild (.getRootNode this) teapot)

(.setTexture mat_brick "ColorMap"
(.loadTexture assetManager "Textures/Terrain/BrickWall/BrickWall.jpg"))
(.setMaterial wall mat_brick)
(apply (memfn setLocalTranslation x y z) wall wall-pos)
(.attachChild (.getRootNode this) wall)

(.detachAllChildren (.getGuiNode this))
(.setSize txt (.. gui-font (getCharSet) (getRenderedSize)))
(.setText txt "Hello World!")
(.setLocalTranslation txt 300 (.getLineHeight txt) 0)
(.attachChild (.getGuiNode this) txt)

(.scale ninja 0.05 0.05 0.05)
(.rotate ninja 0 -3 0)
(apply (memfn setLocalTranslation x y z) ninja ninja-pos)
(.setDirection sun (Vector3f. -0.1 -0.7 -1))
(.attachChild (.getRootNode this) ninja)
(.addLight (.getRootNode this) sun)))))

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

As usual, feel free to factor out the let expression into a series of global defs to play around in the REPL with. When you run this, you should see the classic OpenGL teapot sitting on top a brick wall with a ninja standing behind, and the text "Hello World" appearing near the bottom of the screen. (Coordinates for the guiNode are still x,y,z, with the xy-origin being the bottom-left rather than the top-left which some readers may be familiar with.)

A neat Clojure trick I want to highly is this line:

(apply (memfn setLocalTranslation x y z) wall wall-pos)

The original Java code spells it out explicitly:


This is inelegant because it encourages magic numbers. Does that method support passing an array of three floats or a Vector3f object instead? Probably the latter, I didn't look, you should go do that, but what if it doesn't support an alternative? You're stuck with the syntax, and if you wanted to keep sane by creating a wall_pos array or vector you would always have to decompose it manually whenever needing to use it. With Clojure's memfn you can get around some of that hairiness--you're still specifying how many args the function expects, but it's independent of what the data is. You could wrap that line into a function and just call it like you'd want, but without the dot: (setLocalTranslation wall wall-pos).

I just wanted to highlight the use of apply in conjunction with memfn. If this were production code, and I had no alternative, I'd rewrite like this instead:

(defn setLocalTranslation [obj [x y z]]
(.setLocalTranslation obj x y z))

Just making life a little easier and having fun while doing it.

This tutorial is still making use of the implicit project path JME3 knows about. As the main page says there are multiple ways to specify alternate data sources for your assets, including "ClasspathLocator", "ZipLocator', "FileLocator", "HttpZipLocator", and "UrlLocator".

That's all I'm covering. The official tutorial goes into some more depth, go check it out!

Back to 2 | On to 4 and 5

Posted on 2012-08-24 by Jach

Tags: clojure, jMonkeyEngine, programming


Trackback URL:

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.