LWJGL3 Display Inside JFrame?

Started by mattparks, December 11, 2015, 18:20:32

Previous topic - Next topic

mattparks

Hey I have been creating a game in LWJGL 3.0.0b and I require a JFrame for a entity editor but I also want a entity preview using LWJGL so I thought I could attach the preview into the frame. There are no obvious API's to do this so has anyone found a way?

Thank you guys in advance,

Cornix

As far as I know there is no way to use LWJGL3 inside Swing/AWT. There is however the possibility to use LWJGL2 inside AWT using a Canvas object. I have also read that you can use LWJGL3 with SWT but I have never tried it myself.

piknikco

You can use the JOGL library to put a GLCanvas within your JFrame, and issue draw calls with LWJGL. I've tried this and had success this way when I needed to use the input system from Swing.

smith

If performance isn't crucial you can just copy the framebuffer of an invisible window into a BufferedImage every frame and draw it with a canvas. I've done this several times and it works fine.

mattparks

Quote from: smith on December 15, 2015, 00:42:20
If performance isn't crucial you can just copy the framebuffer of an invisible window into a BufferedImage every frame and draw it with a canvas. I've done this several times and it works fine.

Ok, so then how would I write OpenGL display pixels into to a BufferedImage? I tried using the code below but all I get is a black screen and when writing to a PNG image I get a black image as well.

ByteBuffer buffer = BufferUtils.createByteBuffer(getWidth() * getHeight() * 4);
			GL11.glReadPixels(0, 0, getWidth(), getHeight(), GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, buffer);
			BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);

			for (int x = 0; x < image.getWidth(); x++) {
				for (int y = 0; y < image.getHeight(); y++) {
					int i = (x + getWidth() * y) * 4;
					int r = buffer.get(i) & 0xFF;
					int g = buffer.get(i + 1) & 0xFF;
					int b = buffer.get(i + 2) & 0xFF;
					int rgb = ((r & 0x0ff) << 16) | ((g & 0x0ff) << 8) | (b & 0x0ff);
					image.setRGB(x, y, rgb);
				}
			}

bobjob

The approach of taking the frame buffer and putting it into a BufferedImage is problematic. LWJGL3 and Mac will cause a crash if you try to use AWT Components. So your code will not be portable (at least with the current version).

That said, if the Entity Editor is for personal use, then just use LWJGL 2.
If it is for distribution purposes, then just create your own simple input dialog GUI components to run in your application at least temporarily.

Personally for my own Entity Editor, I just use simple Java Frame with a text area to output its stats and and JButtons for its settings changes. I then open up my own modified simple tile map editor, place the entity in a map, I then test the Entity in game by loading the map. If you want to test your Entity in your entity editor you can always get Java to execute an external application to load the entity file via the cmd line for testing purposes. This should be a smooth enough process as a single entity takes no time to load.

If you have ever executed an external application in Java you would see that you can get the console output from one application as String, in order to communicate with the original application, (in case the entity file is updated in the 3D viewer and you need to reload it in the AWT Application for example). Or an even simpler approach is that the original AWT application would lock (hide) up until the 3D viewer terminates, then auto updates from the file.

mattparks

Quote from: bobjob on December 29, 2015, 02:39:09
The approach of taking the frame buffer and putting it into a BufferedImage is problematic. LWJGL3 and Mac will cause a crash if you try to use AWT Components. So your code will not be portable (at least with the current version).

That said, if the Entity Editor is for personal use, then just use LWJGL 2.
If it is for distribution purposes, then just create your own simple input dialog GUI components to run in your application at least temporarily.

Personally for my own Entity Editor, I just use simple Java Frame with a text area to output its stats and and JButtons for its settings changes. I then open up my own modified simple tile map editor, place the entity in a map, I then test the Entity in game by loading the map. If you want to test your Entity in your entity editor you can always get Java to execute an external application to load the entity file via the cmd line for testing purposes. This should be a smooth enough process as a single entity takes no time to load.

If you have ever executed an external application in Java you would see that you can get the console output from one application as String, in order to communicate with the original application, (in case the entity file is updated in the 3D viewer and you need to reload it in the AWT Application for example). Or an even simpler approach is that the original AWT application would lock (hide) up until the 3D viewer terminates, then auto updates from the file.

This problem might also apply to my screenshot tool it uses a similar method to capture pixels and saves the image but that only gives me a black image as well. Its the same code as before causing the problem. It worked fine in LWJGL2 but now it seams reading pixels is broken or at the least changes. I have a entity editor with a Image displayed in it to show what I am trying to accomplish, now every frame I just have to update the image. http://imgur.com/ycXk1Yl Thanks for the help!

spasi

If you only need to write screenshots, you can use STBImageWrite instead of BufferedImage and ImageIO.

mattparks

Quote from: spasi on December 29, 2015, 16:31:11
If you only need to write screenshots, you can use STBImageWrite instead of BufferedImage and ImageIO.
I still need to write a image to a JFrame somehow, I was just pointing out how I wrote to my canvas was the same way I created screenshots and how both result in black images. I then narrowed it down the problem to the glReadPixels function or how I read from my ByteBuffer.

smith

GL11.glReadPixels(0, 0, getWidth(), getHeight(), GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, buffer);


Note: that will read the bytes tightly packed so there are 3 bytes per pixel not 4.
Other than that your code looks fine but there are much faster ways to write bulk data into a BufferedImage, setRGB is pretty slow.

I tend to use:

ByteBuffer nativeBuffer = BufferUtils.createByteBuffer(w*h*3);
	BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
	GL11.glReadPixels(0, 0, w, h, GL12.GL_BGR, GL11.GL_UNSIGNED_BYTE, nativeBuffer);
	byte[] imgData = ((DataBufferByte)image.getRaster().getDataBuffer()).getData();
	nativeBuffer.get(imgData);


I can get 60fps easily in a JFrame using this method.