James Williams
LinkedInMastodonGithub

Using jMonkeyEngine with Griffon

Tags: 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