The Artima Developer Community
Sponsored Link

Musings on Language and Design
Java 3D Fun with Groovy
by Jeremy Meyer
June 2, 2010
Summary
A quick play around with the Java3D library and Groovy to see how much easier we can make the library to use.

Advertisement

Java 3D Fun with Groovy

If you are anything like me, you were fascinated and enthralled by the great 3D graphics in "Avatar" (and disappointed by the story). Occasionally, when I see graphical achievements like that, I am inspired to download the latest version of Blender, and see if I can render something superb myself. I had a similar moment after watching "Monsters Inc."

I always discover that I can't, because as well as the obvious need to learn the finer details of a very complex world (of translations, textures and shading etc.) it also means being right-brained or creative. Not my speciality.

This time, though, I found myself browsing for 3D images, libraries and tools, and I came across a link on instructables.com explaining how to export data from Blender to Java3D. This sparked my interest and made me wonder how Groovy would work with Java3D, and whether we could get any leverage from it.

I thought I would play with Java3D a bit first, and see if I could create something fun. I was disappointed to find it horrendously complex. Sure, there are an enormous number of parameters that have to be added to a 3D scene, but shouldn't an API help a bit? It really felt like everything had to be done from first principals.

Here is the minimum code you need to show something in Java 3D. I have translated it into Groovy, which in this case, amounted to removing the semi-colons and replacing the types in front of every initialisation with a def statement. Neater than the Java, but no fewer lines.

package simple

import com.sun.j3d.utils.universe.SimpleUniverse
import com.sun.j3d.utils.geometry.ColorCube
import javax.media.j3d.BranchGroup


class ColourCube {
	public ColourCube() {
		def universe = new SimpleUniverse()
		def group = new BranchGroup()
		group.addChild(new ColorCube(0.1))
		universe.getViewingPlatform().setNominalViewingTransform()
		universe.addBranchGraph(group)   
	}

	public static void main( String[] args ) {    
		new ColourCube()
	}
}

I ran this quite easily in Eclipse on my Windows 7 machine, after downloading and installing Java3D. I had to add a project reference to the jar files j3dcore.jar, j3dutils.jar and vecmath.jar and under the Java build path in the project properties, I had to choose the library tab, JRE System Library and then add a Native Library Location which pointed to the bin directory of the Java3D install.

I got this:

The results are somewhat underwhelming, mostly because the front face of the cube is coplanar with the viewing plane, and so it just looks like a square.

A lot of work

This also looked like a lot of work for a not very pretty result, and it struck me that creating a new universe and adding a new branchgroup and a new shape and a new transform etc. must be a sequence of operations that is performed again and again, and it should probably much simpler. We could even come up with a simple DSL that would let us do it. Groovy's Builders are perfect for this, and in my next blog, I will talk about how we can make a simple builder that allows us to create graphical graphs with simple, clear, structured syntax. For now, though, I am still on a mission to have some fun with Java3D and Groovy. So how to do this?

More Fun

The little tutorial at http://www.instructables.com/id/Using-Blender-To-Create-Java3D-Models/ described how to download some Blender data, and export it to a Java3D readable data source, using a Python Script. If you don't know Blender, you won't know that its scripting language is Python. This allows immense power and flexibility when creating animations. (Any chance of a Groovy plugin anyone?).

Having a penchant for the macabre, I downloaded this data for a skull, made publicly available by its kind creator here: http://e2-productions.com/repository/modules/PDdownloads/visit.php?cid=1&lid=58. Loading it up in Blender, I got this rather pleasing result:

Now I am a real ignoramus when it comes to Blender, but the point was to follow the tutorial and just export the data into Java3D readable format. This wasn't too hard. The tutorial directed me to a sourceforge project at http://sourceforge.net/projects/blend2java/ which allows you to do the conversion. I found it easy enough to work out how to run a Python script in Blender (you need to show the script panel and open a text file) and the result was two xml files, Skull_Head.000.Skull.Shape3D.xml and Skull_Head.000.Mandible.Shape3D.xml. get them in a zip file here.

Loading Up the Data

The Java example in the tutorial was somewhat confused, and certainly not minimal. The author, by his own admission was new to Java (and anyway, I wanted it in Groovy) so I rewrote the code, in a somewhat hacky, skull specific way as follows:
package simple

import java.beans.XMLDecoder
import com.sun.j3d.utils.universe.SimpleUniverse
import javax.media.j3d.*
import javax.vecmath.*
import java.awt.Color


public class LoadedShape extends Shape3D {
	public LoadShape () {

	}
	Shape3D loadShape(String filename) {
		def de = new XMLDecoder(
					new BufferedInputStream(
	                       new FileInputStream(filename)))
	    def ls = de.readObject()
	    de.close()
		ls
	}
	public static void main (String[] args) {
		def universe = new SimpleUniverse()
		def group = new BranchGroup()
		def loadedShapes = []
		def ls1 = new LoadedShape().loadShape(args[0])
		def ls2 = new LoadedShape().loadShape(args[1])
		group.addChild(ls1)
		def jawTg = new TransformGroup()
		jawTg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE)
		jawTg.addChild(ls2)
		group.addChild(jawTg)
		Light light = new DirectionalLight(new Color3f(Color.BLUE), new Vector3f(1, -1, -1))
		light.setInfluencingBounds(new BoundingSphere())
		group.addChild(light)
		universe.addBranchGraph(group)
		def t3d = universe.getViewingPlatform().getViewPlatformTransform()
		def t1 = new Transform3D()
		t3d.getTransform(t1)
		def jawTransform = new Transform3D()
		jawTg.getTransform(jawTransform)
		t1.setTranslation new Vector3f(0.0f, 1.8f, 18f)
		t3d.setTransform t1
		50.times{
			3.times { angle ->
			jawTransform.rotX (angle / 10.0)
			jawTg.setTransform jawTransform
			Thread.sleep 200
			}
		}
	}
}

Definitely hacky, loading up the two files separately, but the loading of the shapes is very simple using the XMLDecoder class.

It took some experimenting to get the position of the skull in the viewing platform right, but it proved the concept and with the addition of a nice, blue light, it produced another pleasing image, this time in the Java3D window:

By adding the jaw to its own TransformGroup, I was able to move it independently of the head (which is obviously why it was rendered in two parts). The two loops at the end of the code above make the jaw go up and down in a comical "yuk yuk" fashion. Perhaps with work I could render the entire scene from Shakespeare's Hamlet, where Hamlet holds up the skull of Yorick and talks to it.

Nice Result, but..

So, it was certainly fun, and I learnt a lot about transform groups and viewing planes etc. but it seems extraordinarily difficult to create a 3D scene in code. As I said earlier, Groovy has just the mechanism to help, in its "Builders".

More about those next time!

Talk Back!

Have an opinion? Readers have already posted 22 comments about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Jeremy Meyer adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Jeremy has been designing and developing software for over 20 years, as well as teaching its mastery. He is fascinated by all aspects of architecture, design and development, the philosophical, the psychological and the aesthetic. He currently heads up the training division at hybris Software, a fast growing and very exciting eCommerce company.

This weblog entry is Copyright © 2010 Jeremy Meyer. All rights reserved.

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use