Monday, August 18, 2008

JRuby + JMonkeyEngine = Hello 3D World

JRuby released version 1.1.3 last month, and I missed it because these days I'm all corporate, and I have limited access to the Internet from work, and little time to get online from home. Speaking of which, I'm on daddy-diaper-duty tonight, so I'll keep this brief so as not to get interrupted...

As Charlie Nutter posted recently, "There are a few areas that remain to be tackled... Refactoring the extensive logic governing how Ruby classes can extend abstract or concrete Java classes. Kresten Krab Thorup contributed this well over a year ago, and it's kinda been an island unto itself. It will take a considerable effort to rework."

Too true, that issue has been a thorn in my virtual side for a while now. I've been interested in transitioning into game programming as a career move, and a lot of the libraries that I've dabbled with use abstract classes that must be extended. At one point, JRuby was incapable of doing that, since a Java object was actually decended from JavaProxy, not the actual Java class being extended.

Not so any more! Tonight I pulled down trunk, and loaded up jirb. I had also built the 3D scene graph library JMonkeyEngine, by all accounts a good one for Java. Rumor has it that Sun's Project Wonderland will be using it instead of Java3D, and they're dropping development on the latter for the time being. Anywho, I was able to place the native libraries in my working directory, require the jars, and...

class MyGame < com.jme.app.SimpleGame
def simpleInitGame
end
end

game = MyGame.new
game.start


and just like that, I had a bunch of output from LWJGL and an empty window! But not just any empty window... an empty window into a virtual world, yet to be populated with 3D things, and a little message in the bottom corner telling me that I was getting about 2K FPS and nothing to display. Hit ESC, and the window went away and Java shut down. Alls well that ends well.

Now, to put something into the virtual world will require more work on JRuby's Java Integration, because SimpleGame uses a protected field called rootNode, and the JRuby subclass can't access that field yet. But, oh, once it can, then it's on!

Here's how you'd do it in Scala:

class MyGame extends com.jme.app.SimpleGame {
def simpleInitGame() = { rootNode attachChild new com.jme.scene.shape.Sphere("my sphere",10,10,5) }
}
var game = new MyGame
game start


Works pretty well. My only gripe with Scala right now is the lack of Ruby-ish method aliasing ala obj.blah=x instead of obj.setBlah(x) in Java Integration land. Scala's val statement does a nice job of adding those setters and getters for pure Scala objects though.

Until JRuby gets protected field access working, this bit of code does the job:

f=javax.swing.JFrame.new 'Blah'
f.default_close_operation=0
b=javax.swing.JButton.new 'Quit'
game=com.jmex.game.StandardGame.new 'test game'
b.add_action_listener do |ev|
game.shutdown
sleep 2
java.lang.System.exit 0
end
f.add b
game.start
f.pack
f.show
state = com.jmex.game.state.DebugGameState.new
sphere = com.jme.scene.shape.Sphere.new('my sphere',10,10,5)
sphere.set_random_colors
sphere.update_render_state
state.root_node.attach_child sphere
com.jmex.game.state.GameStateManager.instance.attach_child state
state.active=true


Now there's a cool prismatic sphere in there. The window closer button is for safety's sake, because the default behavior of DebugGameState is to immediately exit Java, which tends to happen in the middle of the OpenGL loop and booches the VM. StandardGame is a move away from SimpleGame into a more OO-ish paradigm, and root_node is a Ruby-ized alias to getRootNode(). But hey, at least it's a working proof-of-concept: JRuby's Java Integration has certainly matured, and you can do some pretty snazzy stuff with it.

1 Comments:

At 11:42 PM, Blogger Jeremy Woertink said...

Hey, I see this post is quite a long time ago, but have you done anymore with this lately? I've started a project, and I'm stuck with using the nifty_gui https://github.com/jwoertink/waves I can't get button interaction to work.

 

Post a Comment

<< Home