# 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
font.BitmapText
light.DirectionalLight
material.Material
math.Vector3f
scene.Geometry
scene.Spatial
scene.shape.Box

system.AppSettings
system.JmeSystem
]))

"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 [; define assets
; teapot in space
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)
wall-pos '(2 -2.5 0)

; line of text
txt (BitmapText. gui-font false)

; ninja model from OgreXML
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"
(.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)

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


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:

        wall.setLocalTranslation(2.0f,-2.5f,0.0f);


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

LaTeX allowed in comments, use $\\...\\$\$ to wrap inline and $$...$$ to wrap blocks.