Use LWJGL in Swing Application

Started by Cornix, February 18, 2014, 23:53:07

Previous topic - Next topic

Cornix

Hi guys,

I have, up to this point, always heard that using Swing with LWJGL was not a good idea because lightweight and heavyweight components dont mix well. However, I have recently read the following tutorial and found out that this is now supported by the Swing library.
Source: http://www.oracle.com/technetwork/articles/java/mixing-components-433992.html

I quickly tested it with a small test application and it really worked!
Even Swing popup menus were correctly displayed on top of the lwjgl canvas.

Now, I am using windows 7 64bit, and I would like to know if this works on all operating systems. I would be glad if others could test this and tell me their results.

Here is the code I used for my test:
public class SwingFrame_LWJGL {
	
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			
			public void run() {
				try {
					SwingFrame_LWJGL window = new SwingFrame_LWJGL();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}
	
	private volatile float[] vertices;
	private JFrame frame;
	private Canvas canvas;
	private Thread gameThread;
	private boolean running;
	private volatile boolean needValidation;
	private volatile boolean needUpdateViewport;
	
	public SwingFrame_LWJGL() {
		frame = new JFrame();
		frame.addWindowListener(new WindowListener() {
			public void windowOpened(WindowEvent arg0) {
			}
			public void windowIconified(WindowEvent arg0) {
			}
			public void windowDeiconified(WindowEvent arg0) {
			}
			public void windowDeactivated(WindowEvent arg0) {
			}
			public void windowClosing(WindowEvent arg0) {
			}
			public void windowClosed(WindowEvent arg0) {
				terminate();
			}
			public void windowActivated(WindowEvent arg0) {
			}
		});
		frame.setTitle("Swing + LWJGL");
		frame.setBounds(100, 100, 1024, 768);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		JMenuBar menuBar = new JMenuBar();
		frame.setJMenuBar(menuBar);
		needValidation = true;
		MenuListener menuListener = new MenuListener() {
			public void menuSelected(MenuEvent e) {
				if (needValidation) {
					needValidation = false;
					frame.validate();
					System.out.println("Validate");
				}
			}
			public void menuDeselected(MenuEvent e) {
			}
			public void menuCanceled(MenuEvent e) {
			}
		};
		
		JMenu mnNewMenu = new JMenu("New menu");
		mnNewMenu.addMenuListener(menuListener);
		menuBar.add(mnNewMenu);
		for (int i = 0; i < 4; i++) {
			JMenuItem menuItem = new JMenuItem("New menu item");
			mnNewMenu.add(menuItem);
		}
		
		JMenu mnNewMenu_1 = new JMenu("New menu");
		mnNewMenu_1.addMenuListener(menuListener);
		menuBar.add(mnNewMenu_1);
		for (int i = 0; i < 4; i++) {
			JMenuItem menuItem = new JMenuItem("New menu item");
			mnNewMenu_1.add(menuItem);
		}
		
		JMenu mnNewMenu_2 = new JMenu("New menu");
		mnNewMenu_2.addMenuListener(menuListener);
		menuBar.add(mnNewMenu_2);
		for (int i = 0; i < 4; i++) {
			JMenuItem menuItem = new JMenuItem("New menu item");
			mnNewMenu_2.add(menuItem);
		}
		
		JMenu mnNewMenu_3 = new JMenu("New menu");
		mnNewMenu_3.addMenuListener(menuListener);
		menuBar.add(mnNewMenu_3);
		for (int i = 0; i < 4; i++) {
			JMenuItem menuItem = new JMenuItem("New menu item");
			mnNewMenu_3.add(menuItem);
		}
		
		frame.getContentPane().setLayout(new BorderLayout(0, 0));
		
		JSplitPane splitPane = new JSplitPane();
		frame.getContentPane().add(splitPane, BorderLayout.CENTER);
		
		JPanel canvasPanel = new JPanel();
		canvasPanel.setLayout(new BorderLayout(0, 0));
		splitPane.setRightComponent(canvasPanel);
		
		canvas = new Canvas() {
			private static final long serialVersionUID = -1069002023468669595L;
			public void removeNotify() {
				stopOpenGL();
			}
		};
		canvas.addComponentListener(new ComponentListener() {
			public void componentShown(ComponentEvent e) {
				setNeedValidation();
			}
			public void componentResized(ComponentEvent e) {
				setNeedValidation();
			}
			public void componentMoved(ComponentEvent e) {
				setNeedValidation();
			}
			public void componentHidden(ComponentEvent e) {
				setNeedValidation();
			}
		});
		canvas.setIgnoreRepaint(true);
		canvas.setPreferredSize(new Dimension(800, 600));
		canvas.setMinimumSize(new Dimension(320, 240));
		canvas.setVisible(true);
		canvasPanel.add(canvas, BorderLayout.CENTER);
		
		JScrollPane scrollPane_1 = new JScrollPane();
		scrollPane_1.setMinimumSize(new Dimension(160, 160));
		splitPane.setLeftComponent(scrollPane_1);
		
		JTree tree = new JTree();
		scrollPane_1.setViewportView(tree);
		
		startOpenGL();
	}
	
	private void setNeedValidation() {
		needValidation = true;
		needUpdateViewport = true;
	}
	
	private void startOpenGL() {
		System.out.println("StartOpenGL");
		
		gameThread = new Thread() {
			public void run() {
				try {
					Display.create();
					Display.setParent(canvas);
					
					Rectangle rect = canvas.getBounds();
					int w = (int) rect.getWidth();
					int h = (int) rect.getHeight();
					
					System.out.println("Display Rectangle: "+rect);
					
					GL11.glClearColor(0.1f, 0.1f, 0.3f, 1);
					
					GL11.glMatrixMode(GL11.GL_PROJECTION);
					GL11.glLoadIdentity();
					GL11.glOrtho(0, w, h, 0, -1, 1);
					
					GL11.glViewport(0, 0, w, h);
					
					setupVertices();
					
					running = true;
				} catch (LWJGLException e) {
					e.printStackTrace();
				}
				while (running) {
					updateGL();
				}
				if (Display.isCreated()) {
					Display.destroy();
				}
			}
		};
		gameThread.start();
	}
	
	private void setupVertices() {
		vertices = new float[4 * 2];
		
		vertices[0] = 0.1f;
		vertices[1] = 0.3f;
		
		vertices[2] = 0.2f;
		vertices[3] = 0.8f;
		
		vertices[4] = 0.9f;
		vertices[5] = 0.6f;
		
		vertices[6] = 0.7f;
		vertices[7] = 0.05f;
	}
	
	private void updateGL() {
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
		
		render();
		
		Display.update();
		Display.sync(60);
		
		if (needUpdateViewport) {
			needUpdateViewport = false;
			
			Rectangle rect = canvas.getBounds();
			int w = (int) rect.getWidth();
			int h = (int) rect.getHeight();
			
			GL11.glMatrixMode(GL11.GL_PROJECTION);
			GL11.glLoadIdentity();
			GL11.glOrtho(0, w, h, 0, -1, 1);
			GL11.glViewport(0, 0, w, h);
		}
		
		int error = GL11.glGetError();
		if (error != GL11.GL_NO_ERROR) {
			String msg = "Unknown Error";
			switch (error) {
			case GL11.GL_INVALID_OPERATION:
				msg = "Invalid Operation"; break;
			case GL11.GL_INVALID_VALUE:
				msg = "Invalid Value"; break;
			case GL11.GL_INVALID_ENUM:
				msg = "Invalid Enum"; break;
			case GL11.GL_STACK_OVERFLOW:
				msg = "Stack Overflow"; break;
			case GL11.GL_STACK_UNDERFLOW:
				msg = "Stack Underflow"; break;
			case GL11.GL_OUT_OF_MEMORY:
				msg = "Out of memory"; break;
			}
			throw new RuntimeException(msg);
		}
	}
	
	private void render() {
		float scale = 100;
		
		GL11.glBegin(GL11.GL_QUADS);
		
		GL11.glColor4f(1, 0, 0, 1);
		GL11.glVertex3f(vertices[0] * scale, vertices[1] * scale, 0);
		
		GL11.glColor4f(1, 0, 0, 1);
		GL11.glVertex3f(vertices[2] * scale, vertices[3] * scale, 0);
		
		GL11.glColor4f(1, 0, 0, 1);
		GL11.glVertex3f(vertices[4] * scale, vertices[5] * scale, 0);
		
		GL11.glColor4f(1, 0, 0, 1);
		GL11.glVertex3f(vertices[6] * scale, vertices[7] * scale, 0);
		
		GL11.glEnd();
	}
	
	private void stopOpenGL() {
		System.out.println("StopOpenGL");
		
		running = false;
		try {
			gameThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	private void terminate() {
		frame.dispose();
		System.exit(0);
	}
	
}


Thank you all in advance!

bmatthew1

Hi Cornix, I have just tested your application on my 64-bit Windows 8 machine and it appears to work fine.


Cornix

Thank you for testing, I really appreciate.

Although it would be nice if some others could test this as well.

imber

Quick test compiled in eclipse with 1.8:



At first the menu did show up in front of the canvas but when I when to do some stuff in the JTree it started showing behind. Will have a look later on

mktiti

Hello,

Not sure if you're still interested in these tests, but anyway here you go:

Ubuntu 14.04 Trusty Tahr 64 bit

It seems to work fine sometimes, but other times the menu showed up behind the lwjgl canvas.

I am also interested in putting the lwjgl context in a swing app, so please tell me you've managed to make it work somehow :D

Cornix

Not quite. Sometimes it works, sometimes it doesnt. So the "solution" I originally had is not really a solution but more of a sideeffect which sometimes takes place.
I have not yet found a way to really make it work without ever having any problems. But I am still looking of course.