Let me present my code: It contains 2 classes: JMonkeyCanvas and QuaternionsAndAngleMatrices. JMonkeyCanvas is being used from within QuaternionsAndAngleMatrices
Code:
public class JMonkeyCanvas extends SimpleApplication {
public Canvas canvas;
private int x, y;
public JMonkeyCanvas(int x, int y) {
super();
this.x = x;
this.y = y;
}
public void initCanvas() {
AppSettings s = new AppSettings(false);
s.setWidth(this.x);
s.setHeight(this.y);
this.setSettings(s);
this.setPauseOnLostFocus(false);
this.createCanvas();
this.startCanvas(true);
Dimension d = new Dimension(this.x, this.y);
this.canvas = ((JmeCanvasContext) this.getContext()).getCanvas();
this.canvas.setSize(d);
this.canvas.setPreferredSize(d);
System.out.println("JMonkeyCanvas initialized");
}
public void simpleInitApp() {
}
public void simpleUpdate() {
}
}
public class QuaternionsAndAngleMatrices extends JPanel {
public static JMonkeyCanvas jmec = new JMonkeyCanvas(500, 500);
private JPanel jmecanvas;
public void createCanvasSpace() {
this.jmecanvas = new JPanel();
this.jmecanvas.setLayout(new FlowLayout());
this.jmecanvas.add(jmec.canvas);
}
public static void main(String[] args) {
jmec.initCanvas();
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
QuaternionsAndAngleMatrices qa = new QuaternionsAndAngleMatrices();
qa.createCanvasSpace();
qa.setLayout(new GridLayout(1, 1, 10, 10));
qa.add(qa.jmecanvas);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
@Override public void windowClosed(WindowEvent e) {
jmec.stop();
}
});
f.getContentPane().add(qa);
f.pack();
f.setVisible(true);
}
});
}
}
however, the above code fails in the method initCanvas in line
actually the exception originates from the call to the method getContext() , as the return value of the method cannot be cast to com.jme3.system.JmeCanvasContext.
if i try to follow your suggestion and use (i hope that is consistent with your intention):
the result is, as expected from the exception: context: com.jme3.system.NullContext
thus the error message originates from com.jme3.system.NullContext and com.jme3.system.JmeCanvasContext being unrelated: JmeCanvasContext is a subinterface of JmeContext, and NullContext only implements JmeContext...
so the real question is: Why is the context of type context:com.jme3.system.NullContext and not of type com.jme3.system.JmeCanvasContext ?
all the javadoc documentation of Application.createCanvas() seems to say, is that after the call to it, the context returned by the method getContext is supposed to be of type JmeCanvasContext .
the tutorial is not that clear to me (otherwise the problem would most propably not exist)
https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:swing_canvas
on the one hand it says, i quote:
Here in the run() method, we start the jME application, create its canvas, create a Swing frame, and add everything together.
on the other, in the associated source code the application seems to be started outside of the run method, by a call to createCanvas() from main . within createCanvas there is pretty much the same as in my method initCanvas . let me cite it here for convenience:
Code:
// Code excerpt from the tutorial swing canvas: TestCanvas
public static void main(String[] args) {
JmeFormatter formatter = new JmeFormatter();
i actually managed to solve the problem like this now…
though i am still not sure what caused the problem in the first place, so if someone has an idea i would still be happy if they could point me in the right direction.
nethertheless i believe this is a fairly elegent solution allowing the canvas to be displayed in any swing application, while any jmonkey application can be passed to it in its constructor.
if anyone has an opinion about the code i would be happy about feedback as well!
thanks!
Code:
public class JCanvas extends JPanel {
private Application app;
private int x, y;
JFrame frame;
public class JCanvasRunner implements Runnable {
private JCanvas jc;
public JCanvasRunner(JCanvas jc) {
this.jc = jc;
}
public void run() {
jc.add(((JmeCanvasContext) jc.app.getContext()).getCanvas(), BorderLayout.CENTER);
}
}
public class JCanvasWindowListener extends WindowAdapter {
private JCanvas jc;
public JCanvasWindowListener (JCanvas jc) {
this.jc = jc;
}
@Override public void windowClosed(WindowEvent e) {
jc.stop();
}
}
public JCanvas(Application app, int x, int y) {
this.app = app;
this.x = x;
this.y = y;
}
public void createFrame() {
this.setLayout(new BorderLayout());
}
public void initCanvas(JFrame frame) {
frame.addWindowListener(new JCanvasWindowListener(this));
AppSettings settings = new AppSettings(true);
settings.setWidth(x);
settings.setHeight(y);
app.setSettings(settings);
app.setPauseOnLostFocus(false);
app.createCanvas();
app.startCanvas();
((JmeCanvasContext) app.getContext()).getCanvas().setSize(x, y);
try {
Thread.sleep(500);
}
catch (InterruptedException ex) {}
SwingUtilities.invokeLater(new JCanvasRunner(this));
}
public void stop() {
app.stop();
}
public static void main(String[] args) {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JCanvas jc = new JCanvas(new Main(), 500, 500);
jc.initCanvas(frame);
frame.getContentPane().add(jc);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
It doesn’t consider threading between the update loop and awt thread, thats also why your sleep() apparently fixes the issue. You have to use app.enqueue for the scene like you have to use SwingUtilities.invokeLater for the awt thread: