JMonkeyEngine and JavaFX

Hello.

I am application programmer.

and live in south korea.

I’m trying to try to develop a modeler or a simple model viewer using JavaFX and JMonkeyEngine.

But, it has not been possible to find I was looking for a case that integrates JMonkeyEngine and Java FX.

Is it not possible to integrate the JMonkeyEngine of JavaFX?

I got a Some experience with Ogre3D engine before.

I think JMonkeyEngine is similar to Ogre3D.

Integration if this is not possible, need to be developed using the 3D API for Java FX if.

thanks to read my topic.

And, I’m sorry my English skills are not enough.

For embedding javafx into jme3, check out http://hub.jmonkeyengine.org/forum/topic/javafx-embedded-in-jme3/
For embedding jme3 into javafx, you will have to go render-to-image route and then display some kind of image view in javafx.
Other possibility is just to have multi-window UI, with jme3 having it’s own dedicated preview window and having jme3 gui floating next to it

1 Like
@abies said: For embedding javafx into jme3, check out http://hub.jmonkeyengine.org/forum/topic/javafx-embedded-in-jme3/ For embedding jme3 into javafx, you will have to go render-to-image route and then display some kind of image view in javafx. Other possibility is just to have multi-window UI, with jme3 having it's own dedicated preview window and having jme3 gui floating next to it

thanks!

it is very useful information for me.

Hi,
in the last couple of weeks I played around with javafx and I really start liking it (I did swing). It would be really neat to find a way of integrate both, Jme and fx for all kinds of non-full-screen desktop applications. However, from what I’ve read, we probably have to hope for an opengl node to ship with Java 8 (see this stackoverflow thread).

Another interesting project I found is lwjgl-fx which basically uses a render-to-image approach as Arthur suggested. It seems, little adaption is needed to use it for Jme.

In the meantime I came up with a quite hackish solution that works for the moment and lets me go on with my project. I use a swing frame (borderless, always on top) and a javafx pane. The dimensions of the swing frame are synched to the dimension of the fx pane via reposition event.

A minimal example would look like this (please excuse my Java, I’m rather a Scala native):

FxWindow.java (contains main method, jme and fx windows are created, reposition events are published on the Eventbus, if size or position of the pane changes)
[java]
package integrationhack;

import java.awt.Canvas;
import java.util.concurrent.Callable;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import jme3test.post.TestRenderToTexture;

import org.bushe.swing.event.EventBus;
import org.bushe.swing.event.EventServiceExistsException;
import org.bushe.swing.event.EventServiceLocator;
import org.bushe.swing.event.ThreadSafeEventService;

import com.jme3.app.SimpleApplication;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeCanvasContext;

public class FxWindow extends Application {

private Stage stage;
private Pane jmePane;
private Scene scene;

private class RepositionListener implements ChangeListener<Object> {

	@Override
	public void changed(ObservableValue<? extends Object> arg0, Object arg1, Object arg2) {
		int x = (int) (stage.getX() + scene.getX() + jmePane.getLocalToSceneTransform().getTx());
		int y = (int) (stage.getY() + scene.getY() + jmePane.getLocalToSceneTransform().getTy());
		int w = (int) jmePane.getWidth();
		int h = (int) jmePane.getHeight();
		EventBus.publish(new JmeRepositionEvent(x, y, w, h));
	}
}

@Override
public void start(Stage stage) throws Exception {
	this.stage = stage;
	VBox root = new VBox();

	MenuBar menuBar = new MenuBar();
	Menu menuFile = new Menu("File");
	Menu menuEdit = new Menu("Edit");
	menuEdit.getItems().add(new MenuItem("Buharrh"));
	menuBar.getMenus().addAll(menuFile, menuEdit);
	root.getChildren().add(menuBar);

	jmePane = new Pane();
	ChangeListener<Object> repoListener = new RepositionListener();
	VBox.setVgrow(jmePane, Priority.ALWAYS);
	root.getChildren().add(jmePane);

	stage.setTitle("JavaFx with JmeFrame");
	scene = new Scene(root, 800, 600);
	stage.setScene(scene);

	stage.setOnCloseRequest(new EventHandler<WindowEvent>() {

		@Override
		public void handle(WindowEvent event) {
			System.exit(0);
		}
	});

	stage.xProperty().addListener(repoListener);
	stage.yProperty().addListener(repoListener);
	stage.getScene().xProperty().addListener(repoListener);
	stage.getScene().yProperty().addListener(repoListener);
	jmePane.widthProperty().addListener(repoListener);
	jmePane.heightProperty().addListener(repoListener);

	stage.show();
}

public static void main(String[] args) {
	try {
		EventServiceLocator.setEventService(EventServiceLocator.SERVICE_NAME_SWING_EVENT_SERVICE,
				new ThreadSafeEventService());
	} catch (EventServiceExistsException e) {
		e.printStackTrace();
	}

	final com.jme3.app.Application rtt = new TestRenderToTexture();
	AppSettings settings = new AppSettings(true);
	settings.setVSync(true);
	rtt.setSettings(settings);
	rtt.createCanvas();
	JmeCanvasContext ctx = (JmeCanvasContext) rtt.getContext();
	Canvas canvas = ctx.getCanvas();
	ctx.setSystemListener(rtt);
	new JmeWindow(canvas);
	rtt.startCanvas();
	rtt.enqueue(new Callable<Void>() {
		public Void call() {
			rtt.setPauseOnLostFocus(false);
			((SimpleApplication) rtt).getFlyByCamera().setDragToRotate(true);
			return null;
		}
	});

	launch(args);
}

}
[/java]

JmeWindow.java (just a borderless frame that listens on reposition events)
[java]
package integrationhack;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;

import org.bushe.swing.event.EventBus;
import org.bushe.swing.event.EventSubscriber;

public class JmeWindow implements EventSubscriber<JmeRepositionEvent> {

private JFrame frame;
private Canvas canvas;

public JmeWindow(Canvas canvas) {

	this.canvas = canvas;
	EventBus.subscribeStrongly(JmeRepositionEvent.class, this);

	EventQueue.invokeLater(new Runnable() {
		@Override
		public void run() {
			try {
				initialize();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	});
}

private void initialize() {
	JPanel panel = new JPanel(new BorderLayout(0, 0));
	panel.add(canvas);
	frame = new JFrame();
	frame.setSize(new Dimension(300, 200));
	frame.getContentPane().add(panel, BorderLayout.CENTER);
	frame.setAlwaysOnTop(true);
	frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
	frame.setResizable(false);
	frame.setUndecorated(true);
	frame.setVisible(true);
}

@Override
public void onEvent(JmeRepositionEvent event) {
	frame.setLocation(event.x, event.y);
	frame.setSize(event.w, event.h);
}

}
[/java]

JmeRepositionEvent.java (used for notifiing the JmeWindow on position updates via Eventbus)
[java]
package integrationhack;

public class JmeRepositionEvent {

int x, y, w, h;

public JmeRepositionEvent(int x, int y, int w, int h) {
	this.x = x;
	this.y = y;
	this.w = w;
	this.h = h;
}

}
[/java]

I know this is only a temporary solution, but it bridges the waiting time for me. I really hope someone comes up with a robust integration in the near future.

If you look at javafx.embed.swing.SwingNode class and at com.sun.javafx.sg.prism.NGExternalNode, with some effort it should be possible to create very high performance embedding. NGExternalNode is repainting directly from ByteBuffer, which probably can be native, meaning that jme3 could render directly into it. Pain, as always, would be with translating mouse/key events into Jme3.

Wow, these are promising news. I had already tried to use a SwingNode with a Jme canvas but with no success.

Regarding NGExternalNode: it seems there is very little documentation on the com.sun.javafx.sg.prism package (unfortunate naming these days anyway :wink:). All I found are the OpenJDK sources on bitbucket. Am I missing something?

There is no documentation, because it is internal Sun API. Embedding one graphical toolkit into another is not something for which you design very official and stable API - it is just not worth it. Any such integration would need to follow javafx developments closely.

I’m not saying that SwingNode should be used directly - but if you look at the sources, it shows how same thing could be achieved with jme3. Think about it as a ‘documentation’ for NGExternalNode :wink: