Using jMonkeyEngine with Griffon
jMonkeyEngine is a scenegraph game engine with a vibrant community. It supports OpenGL functionality via LWJGL, sound via OpenAL, and keyboard/mouse/controller interactivity using JInput. In this post, we are going to embed a jME canvas in Griffon and draw a textured cube.
Getting started
- Download LWJGL and drop the lwjgl.jar in your lib directory.
- Find the native files for your operating system and extract them into lib/native
- Download jME 2.0 Distribution. We aren't doing that much advanced stuff so we only need jme.jar, jinput.jar, and jme-awt.jar. Drop these into your lib directory.
Modify your Config.groovy file to contain the following:
griffon { app { javaOpts = ["-Djava.library.path=${basedir}/lib/native"] } extensions { jarUrls = [] jnlpUrls = ["lwjgl.jnlp"] } }
Add this jnlp file to griffon-app/conf/webstart: lwjgl.jnlp.
Switch the order in Application.groovy to the following:
mvcGroups { '<Your app name>' { model = 'YOURAPP-Model' controller = 'YOURAPP-Controller' view = 'YOURAPP-View' } }
It's important that the controller be initialized before the view because the controller will initialize our canvas.
Linking our model and view
JmeSwingModel.groovy
import groovy.beans.Bindable
class JmeSwingModel {
@Bindable canvas
@Bindable impl
@Bindable display
int height = 640
int width = 480
}
JmeSwingView.groovy
application(title:'Griffon jME Swing',
pack:true,
resizable:false,
//location:[50,50],
locationByPlatform:true,
iconImage: imageIcon('/griffon-icon-48x48.png').image,
iconImages: [imageIcon('/griffon-icon-48x48.png').image,
imageIcon('/griffon-icon-32x32.png').image,
imageIcon('/griffon-icon-16x16.png').image]
) {
widget(model.canvas)
}
Initializing our canvas
JmeSwingController.groovy
import com.jme.input.KeyInput
import java.awt.event.KeyListener
import com.jme.input.InputHandler
import com.jme.system.DisplaySystem
import com.jmex.awt.lwjgl.LWJGLCanvas
import com.jme.system.canvas.JMECanvas
import java.awt.event.ComponentListener
import com.jmex.awt.input.AWTMouseInput
import com.jme.system.lwjgl.LWJGLSystemProvider
import com.jme.system.canvas.JMECanvasImplementor
import com.jmex.awt.lwjgl.LWJGLAWTCanvasConstructor
class JmeSwingController {
// these will be injected by Griffon
def model
def view
void mvcGroupInit(Map args) {
// make the canvas:
model.display = DisplaySystem.getDisplaySystem(LWJGLSystemProvider.LWJGL_SYSTEM_IDENTIFIER);
model.display.registerCanvasConstructor("AWT", LWJGLAWTCanvasConstructor.class);
model.canvas = (LWJGLCanvas)model.display.createCanvas(model.width, model.height);
model.canvas.setUpdateInput(true);
model.canvas.setTargetRate(60);
// Setup key and mouse input
KeyInput.setProvider(KeyInput.INPUT_AWT);
KeyListener kl = (KeyListener) KeyInput.get();
model.canvas.addKeyListener(kl);
AWTMouseInput.setup(model.canvas, false);
// Important! Here is where we add the guts to the panel:
model.impl = new MyImplementor(model.width, model.height);
model.canvas.setImplementor(model.impl);
model.canvas.setBounds(0, 0, model.width, model.height);
}
}
Drawing on our canvas
MyImplementor is bit of code that draws our world.
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.InputHandler;
import com.jme.input.KeyInput;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.Renderer;
import com.jme.scene.shape.Box;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.system.canvas.JMECanvas;
import com.jme.system.canvas.JMECanvasImplementor;
import com.jme.system.canvas.SimpleCanvasImpl;
import com.jme.system.lwjgl.LWJGLSystemProvider;
import com.jme.util.GameTaskQueueManager;
import com.jme.util.TextureManager;
import com.jmex.awt.input.AWTMouseInput;
import com.jmex.awt.lwjgl.LWJGLAWTCanvasConstructor;
import com.jmex.awt.lwjgl.LWJGLCanvas;
public class MyImplementor extends SimpleCanvasImpl {
private Quaternion rotQuat;
private float angle = 0;
private Vector3f axis;
private Box box;
private InputHandler input;
public MyImplementor(int width, int height) {
super(width, height);
}
public void simpleSetup() {
// Normal Scene setup stuff...
rotQuat = new Quaternion();
axis = new Vector3f(1, 1, 0.5f);
axis.normalizeLocal();
Vector3f max = new Vector3f(5, 5, 5);
Vector3f min = new Vector3f(-5, -5, -5);
box = new Box("Box", min, max);
box.setModelBound(new BoundingBox());
box.updateModelBound();
box.setLocalTranslation(new Vector3f(0, 0, -10));
box.setRenderQueueMode(Renderer.QUEUE_SKIP);
rootNode.attachChild(box);
box.setRandomColors();
TextureState ts = renderer.createTextureState();
ts.setEnabled(true);
ts.setTexture(TextureManager.loadTexture(this.getClass().getClassLoader().getResource(
"Monkey.jpg"),
Texture.MinificationFilter.BilinearNearestMipMap,
Texture.MagnificationFilter.Bilinear));
rootNode.setRenderState(ts);
input = new InputHandler();
}
public void simpleUpdate() {
input.update(tpf);
// Code for rotating the box... no surprises here.
if (tpf < 1) {
angle = angle + (tpf * 25);
if (angle > 360) {
angle = 0;
}
}
rotQuat.fromAngleNormalAxis(angle * FastMath.DEG_TO_RAD, axis);
box.setLocalRotation(rotQuat);
}
}
Download the source here