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

Using Processing with Griffon

Processing is a great graphics language that reminds me alot of old-school BASIC/turtle graphics. It's built on top of Java so it's only natural that we'd eventually try it with Griffon.

Getting Started

  • Download processing from http://processing.org/download.
  • Regardless of your operating system, grab the Windows version WITHOUT Java.
  • Find the core.jar file and drop it in the lib directory of your project.

Extend PApplet

The following is a slightly tweaked example from processingjs.org
 

import processing.core.*;

public class Embedded extends PApplet {
// Global variables

    float radius = 50.0f;
    int X, Y;
    int nX, nY;
    int delay = 16;

// Setup the Processing Canvas
    void setup() {
        size(400, 400);
        strokeWeight(10);
        frameRate(15);
        X = width / 2;
        Y = width / 2;
        nX = X;
        nY = Y;
    }

// Main draw loop
    void draw() {

        radius = radius + sin((float) (frameCount / 4));

        // Track circle to new destination
        X += (nX - X) / delay;
        Y += (nY - Y) / delay;

        // Fill canvas grey
        background(100);

        // Set fill-color to blue
        fill(0, 121, 184);

        // Set stroke-color white
        stroke(255);

        // Draw circle
        ellipse(X, Y, radius, radius);
    }

// Set circle's next destination
    void mouseMoved() {
        nX = mouseX;
        nY = mouseY;
    }
}

Linking your view and model to Processing

Proce55ingModel.groovy

import groovy.beans.Bindable
import processing.core.PApplet

class Proce55ingModel {
   @Bindable pApplet = new Embedded()
}

Proce55ingView.groovy

application(title:'Proce55ing',
  size:[400,400],
  //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.pApplet)
}

Final touches

Proce55ingController.groovy

class Proce55ingController {
    // these will be injected by Griffon
    def model
    def view

    void mvcGroupInit(Map args) {	
        // ensures animation is started and that vars properly set
        model.pApplet.init()
		
    }
    /*
    def action = { evt = null ->
    }
    */
}

Screenshot of pulsing circles.

Because it is very procedural like OpenGL, a builder might be improbable.

Groovy's duck-typing and the fact that Processing uses floats for its non-integral numeric type means that some values will need to be cast. With a few casts, you can use any of the examples on processingjs.org

Download the source code here.

Introducting TweetGames

I like to think of TweetGames as a way to interact with Twitter without tweeting every game action to the world. And a way to scratch an itch. The first game is a simple wordsearch generated from Twitter Trends for that given hour. I'll be eventually releasing more to play around with experimental ways of consuming Twitter data.

TweetGames uses Grails on top of the Google App Engine and a front-end using the 2D graphics library Pulpcore. One of the more interesting things other than the stretching your brain has to do to not rely on a RDBMS crutch is how efficient the Groovy syntactic sugar closures are. While I was trying to get key requests under the 30 second limit, I found that replacing some for-loops with find,findAll,or collect closures cut between .718 and 1.3secs from the request runtimes.

Check it out at http://tweetgames.appspot.com or on Facebook at http://bit.ly/tweetgames_facebook

Adding Growl notifications to Griffon applications

One of the things I am reminded of when WWDC rolls around, other than Silicon Valley's crush on all things Apple, is the extra little bit of polish in OS X applications. Part of that polish is the Growl notification system and adding Growl notifications is one way to make your applications feel more native. It exists in some shape or form on all three major platforms. I'll be demonstrating how to integrate Growl in a Mac or Linux Griffon application, Windows will have to wait until I have the time to unify targeting the three in a plugin.

Firstly, Make sure you have growlnotify(OS X) or libnotify-bin(Ubuntu) installed.

Next, add the following script to your src directory of your app:

 
public class Notifier {
	final static String OS_NAME = System.getProperty("os.name")
	
	static void sendNotification(title, msg, icon) {
		try{
		if (OS_NAME.equals("Linux")) {
			def cmd = "notify-send ${title} ${message} -i ${icon}"
			cmd.execute()
		} else if (OS_NAME.equals("OSX")) {
			def cmd = "growlnotify -n ${title} -I ${icon} -m ${message}"
			cmd.execute()
		}
		} catch (IOException ex) {
			// very possible that the required package (libnotify-bin on Linux or growlnotify on OS X) is not installed
		}
	}
}

Now you can call your Notifier from your controller just like any other class. Calling the command-line versions directly is a bit messy but it is probably what the Java libs are doing anyways.

In a plugin, possibly a hook could be added to installer scripts to ask if the user would like to receive growl-like notifications and then install the requisite packages.

What else do you think a growl plugin should do? Comment on this post or better yet on user@griffon.codehaus.org

For those that are curious, there is Growl for Windows.

Thanks to  Colin Harrington and Marc Palmer for the inspiration.

GriffonCast Simple Video Player

In this first Griffon Cast, I show you how to create a simple video player using Griffon in less than 10 minutes. If you'd like to try this on a Mac, see the links below. For those impatient souls, you can download the source code here.

(Version française, ci-dessous.)

Dans ce premier Griffon Cast, je vous montre comment de faire un lecteur vidéo rapidement en moins de dix minutes. Comme j'ai dit dans le video, GStreamer marche seulement sur Linux et quelquefois sur OS X (lien ci-dessous). Pour eux qui sont très impatients, vous pouvez télécharger le source code ici.


GStreamer:

http://gstreamer.org


GStreamer on OS X:

http://gstreamer.darwinports.com/


Summer of Code 2009 Projects Announced

Congratulations to Hjalmar Ekengren and Alexey Lapusta whose projects to implement Groovy on Android and improve Groovy support in Eclipse, respectively, were accepted. MrG will be the mentor for both students.

Pulpcore Word Search Game

I've been playing around with the 2D graphics library Pulpcore making a simple word search game. I have to say it actually made developing an applet-based game really fun. And best of all, it loads lightning fast, it's really too bad that adding Groovy would add several MBs to the download.

 

Groovy Sydney Presentation

Watch my presentation at Sydney Groovy User Group here:

JavaOne 2009 and Sydney Groovy

 In case you haven't already heard from Twitter, Griffon will be represented in a BOF lead by Danno Ferrin and myself.

Next month, I'll be leading a presentation for the Sydney Groovy Group. The location hasn't been announced by the date and time is March 25th at roughly 7P local time(8AM UTC/1AM PST/4AM EST). The video will be up shortly after that you should try to attend (if you're in Sydney) or catch the live stream(on this blog) as I'll be answering questions after the presentation.

OpenSocial with Java

Though I wasn't able to release an app this weekend, nonetheless Weekend Apps - Opensocial was a great experience. For those that know nothing of the technology theme, Weekend Apps is an efficent way to get weeks of experience in a weekend having industry experts there to help you through problems.

One of the "a-ha" moments for me was learning that OpenSocial gadgets can run arbitrary html and thus can have embedded applets (as they already support embedded flash). For a user with Java 6 installed, the ability to drag the gadget outside the OpenSocial container and continue to run it after leaving the page might make for a killer app.

I was able to very quickly get a draggable gadget working using sample code from Sun. Code here

The only caveat is that because applets autostart, some care needs to be taken to handle things if the user minimizes a gadget then expands it. One might have the landing page be a static image of the app that the user would have to click to run it.