JavaOne 2014 – Having fun with Lego & JavaFX 3d


More Lego elements

While I learned last week that there’s a difference between Hands-On-Lab and Tutorial at JavaOne I’ve already prepared all the content for Developing JavaFX RCP with the Eclipse4 Application Platform so I will slightly change my plan and provide you a walk through how to develop an application similar the one at the end of this post – if you want you can even follow a long because I’ll post the source codes before the tutorial.

In my last post I described in short that we made up a DSL to define lego bricks and assemble bigger buildings from it – we really had so much fun with and so we added some more extra features to the DSL.

First we modeled (in freecat) a door frame brick:

2_door

Afterwards we defined a new model type – until then we only had bricks who completely followed the lego raster – and named them mounted parts. An example of such a mounted part is the door which is mounted on to the door frame.

2_door_mounted

– we left the door a bit open in this screenshot by setting the y-angle to 160 instead of 180.

Now that’s it for the moment on the definition side, we’ll comeback later to it once more.

The viewer

Having now all the 3d models we need a viewer for none developers. We’ll develop such an application as part of our tutorial at JaveOne but a bit more involved version can be seen in this video.

This is an application developed using e4 + e(fx)clipse JavaFX renderers and other e(fx)clipse opensource components, while in the case it showing our lego 3d model it could be ANY 3d model from engines to your smart-home setup – I’ll talk a bit about this app in the JavaFX at Eclipse.org talk where I also talk about the lego language, DSLs and JavaFX, Testing JavaFX, … .

Add some interactivity

When we had added this mounted stuff, we immediately had the idea to let those mounted parts modify their state while shown in the 3d model viewer in the e4 application above, so we added the possibility to define actions in the DSL (support is not completely ready and needs some more changes to support everything).

For our door we defined 2 actions like this:

2_door_actions

If we now load the FXML-Model once more things like this will happen (turn on sound on your computer to get the full effect)

The trick is that while the 3d Viewer itself is only able to display 3 models and animate them it knows nothing about those actions we are encoding the logic in a JavaScript file we are naming equal to the FXML-File and when the FXML-File is loaded through a service fire up Nashorn, load the JavaScript file and we are ready to go. The JavaScript file we are generating looks similar to this one:

var Media 		= Java.type("javafx.scene.media.Media");
var MediaPlayer = Java.type("javafx.scene.media.MediaPlayer");
var Transition 	= Java.type("org.eclipse.fx.ui.animation.BaseTransition");
var Consumer 	= Java.type("java.util.function.Consumer");
var Duration 	= Java.type("javafx.util.Duration");

var PropertyTransition = Java.extend(Transition);
		
		
function getRotateNode(node) {
	return node.getParent().getParent();
}
		
function getTransform(node,x,y,z) {
	for( var i = 0; i < node.getTransforms().size(); i++ ) {
		var t = node.getTransforms().get(i);
		if( t.getClass().getSimpleName().equals("Rotate") ) {
			if( t.getAxis().getX() == x 
				&& t.getAxis().getY() == y
				&& t.getAxis().getZ() == z ) {
				return t;
			}
		}
	}
	return null;
}

function rotateY(node, targetAngle, soundFile, duration) {
	var r = getTransform(node, 0, 1, 0);
	if( r != null ) {
		var start = r.getAngle();
		var delta = start - targetAngle;
		var transition = new PropertyTransition(Duration.millis(duration)) {
			interpolate: function(frac) {
				r.setAngle(start - frac * delta);
			}
		};
		transition.play();
		if( soundFile != null ) {
			var m = new Media(soundFile);
			var mediaPlayer = new MediaPlayer(m);
			mediaPlayer.play();
		}
	} 
}
		
function doorOpen(event) {
	var node = getRotateNode(event.target);
	rotateY(node,80.0,"file:/Users/tomschindl/git/elementary/sample/lego/3d/squeaking-door.mp3",3000);
}

function doorClose(event) {
	var node = getRotateNode(event.target);
	rotateY(node,180.0,null,3000);
}

function init() {
	var InitComsumer = Java.extend(Consumer);
	return new InitComsumer() {
		accept : function(n) {
			var dynamic = n.lookup("#dynamic_1");
			dynamic.setOnMousePressed(doorOpen);
		}
	};
}

init();

I need to clean up the sources of the Lego-Language (e.g. removal of absolute paths, …) a bit before pushing it to our github account but you can clone the repo from there already.

This entry was posted in e(fx)clipse. Bookmark the permalink.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.