lwjgl with JPanel

Started by gmseed, November 03, 2011, 21:31:04

Previous topic - Next topic

gmseed

Hi

I've worked my way through the samples files and all of them place the Display calls in a main() class.

What about placing the display on a Japenl that sits inside a JFrame, as this is a fairly common way to work.

The only page I can find is:

http://lwjgl.org/wiki/index.php?title=Using_a_Resizeable_AWT_Frame_with_LWJGL

but this looks like a sledge hammer to crack a nut.

Is there a simpler way?

Without using lwjgl and straight-JOGL I'd normally drop a GLCanvas or GLPanel onto a JFrame.

Thanks

Graham

kappa

There is also the option to use AWTGLCanvas.

gmseed

Hi

Thanks for your reply.

So, I thought I'd try using AWTGLCanvas to get a working example, which could maybe added to your samples folder.

The code below consists of AWTGLCanvas_Example  which is the main JFrame.

MyAWTGLCanvas extends AWTGLCanvas, which is placed on the frame.

When I run the code I get a black window and don't see the quad. Does anyone know why? When I place breakpoints in initGL() and paintGL() I discover that they are not being called.

Note the call:

mCanvas.start();

after all of the initialisation, otherwise an exception is thrown that the parent must be visible before Display is called.

Note also the commented out code:

       // init OpenGL here
       //LWJGLRenderer renderer = new LWJGLRenderer();

           //newDim = newCanvasSize.getAndSet(null);

              //GL11.glViewport(0, 0, newDim.width, newDim.height);
              //renderer.syncViewportSize();

which are referred to in the example:

http://lwjgl.org/wiki/index.php?title=Using_a_Resizeable_AWT_Frame_with_LWJGL

but not defined.

PS. I used the QuadExample code as the basis for choosing something to render. Also, so that there is the current example of QuadExample that runs in its own window and this example can compare the differences regarding setup.

Thanks

Graham

=====================

package my_lwjgl;

import java.awt.BorderLayout;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import java.awt.Canvas;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.AWTGLCanvas;
import org.lwjgl.opengl.Display;
import org.lwjgl.util.Dimension;

public class AWTGLCanvas_Example extends JFrame
{
   private JPanel          contentPane;
   private MyAWTGLCanvas   mCanvas;

   /**
    * Create the frame.
    */
   public AWTGLCanvas_Example()
   {
       setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       setBounds(100, 100, 450, 300);
       contentPane = new JPanel();
       contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
       contentPane.setLayout(new BorderLayout(0, 0));
       setContentPane(contentPane);
       try
       {
           mCanvas = new MyAWTGLCanvas(this);
       }
       catch (LWJGLException e)
       {
           e.printStackTrace();
       }
       contentPane.add(mCanvas, BorderLayout.CENTER);
       this.setVisible(true);
       mCanvas.start();
   }

   /**
    * Launch the application.
    */
   public static void main(String[] args)
   {
       EventQueue.invokeLater(new Runnable() {
           public void run() {
               try {
                   AWTGLCanvas_Example frame = new AWTGLCanvas_Example();
                   frame.setVisible(true);
               } catch (Exception e) {
                   e.printStackTrace();
               }
           }
       });
   }

} // class AWTGLCanvas_Example

================


package my_lwjgl;

// lwjgl
import javax.swing.JFrame;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.AWTGLCanvas;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.Dimension;

public class MyAWTGLCanvas extends AWTGLCanvas
{

   protected JFrame    mParent;
   
   public MyAWTGLCanvas(JFrame parent) throws LWJGLException
   {
       super();
       mParent = parent;
   }
   
   public void start()
   {
       try
       {
           Display.setDisplayMode(new DisplayMode(800,600));
           
           Display.setParent(this);
           Display.setVSyncEnabled(true);
           
           Display.create();
       }
       catch (LWJGLException e)
       {
           e.printStackTrace();
           System.exit(0);
       }

       // init OpenGL here
       //LWJGLRenderer renderer = new LWJGLRenderer();

       Dimension newDim = null;
       
       while (!Display.isCloseRequested())
       {
           //newDim = newCanvasSize.getAndSet(null);
           
           if (newDim != null)
           {
              //GL11.glViewport(0, 0, newDim.width, newDim.height);
              //renderer.syncViewportSize();
           }

           // render OpenGL here
           Display.update();
       }
       
       Display.destroy();
   }
   
   // Override this to do initialising of the context. It will be called once from paint(), immediately after the context is created and made current.
   public void initGL()
   {        
       // init OpenGL
       GL11.glMatrixMode(GL11.GL_PROJECTION);
       GL11.glLoadIdentity();
       GL11.glOrtho(0, 800, 600, 0, 1, -1);
       GL11.glMatrixMode(GL11.GL_MODELVIEW);      
   }
   
   // Override this to do painting
   protected void paintGL()
   {
       // Clear the screen and depth buffer
       GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);  
       
       // set the color of the quad (R,G,B,A)
       GL11.glColor3f(0.5f,0.5f,1.0f);
           
       // draw quad
       GL11.glBegin(GL11.GL_QUADS);
           GL11.glVertex2f(100,100);
           GL11.glVertex2f(100+200,100);
           GL11.glVertex2f(100+200,100+200);
           GL11.glVertex2f(100,100+200);
       GL11.glEnd();
   }
} // class MyAWTGLCanvas

broumbroum

hello gmseed,
if you want to use AWTGLCanvas, here are some things to know :
- do not use Display*() functions, but implement add-removeNotify(), initGL() and paintGL();
- create components and render on the AWT or Swing rendering Thread (i.e. SwingUtilities.invoke* or javax.swing.Timer.scheduleAt*()).

if you want to use Display*() :
- do not use AWTGLCanvas.

cu

gmseed

Hi

Thanks for your reply.

It's clearly going to take some time and numerous posts to establish a good example which several newbies could use!

I'll have another go at getting a basic example working.

Doesn't NOT using Display in a way ask the question "Why bother using lwjgl?" If I can't use Display with AWTGLCanvas then I might as well use GLJPanel with a JPanel or the awt equivalent GLCanvas.

I must confess I'm getting confused as all of the website examples use Display.

Graham

kappa

You can find examples for AWTGLCanvas in the source of LWJGL test package. here.

A little clarification should help here. LWJGL has two different methods for using LWJGL with AWT. They work very differently and have various advantages/disadvantages over each other.

1) The Display.setParent() method, basically creates a native window which sticks to the AWT Canvas, it has the speed, stability of the native window, its bit more compatible in some ways (in that you can disable all AWT drawing and have it still work), etc. However you need to use LWJGL's native Keyboard and Mouse classes to get input and can't use AWT's keyboard and mouse stuff, as you rightly point out above the API is a bit more tricky and long winded to use, only one native window can be created a time due to its static nature.

2) AWTGLCanvas has a much simpler API which is more familiar to AWT users, you can create multiple instances of it, it uses none of the native window code (entirely AWT for the window code), it can use AWT input classes. However it is a little slower as drawing is limited to the EDT/AWT drawing rate, all OpenGL calls must be called in or from the paintGL() so a little more ridged.

A bit more detailed comparison between the two here.

Then again if you can avoid mixing OpenGL and AWT and use just the native Display window it would be a nicer solution. These days the OpenGL GUI options are pretty solid with libraries like TWL and Nifty Gui.

Hope that helps.

gmseed

Hi

Thanks for your reply but I'm still no clearer as to how, if it is possible, to use lwjgl within, say, a JPanel placed on a JFrame using the key element of lwjgl Display, such that the opengl JPanel window is resizeable.

Surely this is a basic requirement isn't it?

The posts have got me distracted from my original post via AWTGLCanvas.

Is it to be concluded that when electing to use opengl within a resizeable window embedded in a JFrame container then don't use lwjgl.

Graham

CodeBunny

One thing to know is that JPanel is Swing and therefore lightweight; AWT is heavyweight. Heavyweight components are associated with their own system resources automatically, so they can be more easily integrated with OpenGL. Thus the AWTGLCanvas.

PrintIn3DNow

Hi gmseed, I'm not sure if you're still following this or not, but I think I'm having a similar internal discussion.

I am trying to build a custom file selection dialog box for 3D STL files. As part of the file selection process, I want the user to be able to see a small preview of their file, and be able to do some basic things. Like rotate and zoom. I'm actually compounding my grief by basing my class on a JDialog managed by WindowBuilder. I have a bunch of stuff for file navigation, a Load and Cancel button set in a pane, and an absolutely located canvas for rendering on another panel. I did decide to at least simplify my problem by making the dialog a fixed size, and the canvas as well.

My problem is that the canvas, after calling setParent and Display.create(), essentially becomes a black hole for events. It took a while for me to figure out that I could generally count on the lostfocus event for the window to some thru, so I could at least destroy() the display and get the app to close. Heaven forbid you drag your mouse across a canvas that's been created and there's no game loop running!

I haven't found a perfect solution yet, but I think I'm getting close to finding something that works, at least some of the time. I've inserted a canvas underneath my rendering canvas that's about 5 pixels larger on all sides. I have a couple of boolean variables I keep track of, and if I'm in a certain state and I see the mouseEnter event on the 'undercanvas', (and the Display.isCreated() ),I start a game-loop. The head of the loop is basically Display.update() followed by scans of the mouse position to make sure the user fully enters the rendering canvas. This has the obvious drawback that the loop can be entered accidentally and not return UNTIL the user enters the area, but I'm working on that. I've found that if I make the undercanvas only 1 or 2 pixels bigger, I have a much lower chance of catching an entered event. It seems like AWT drops messages a lot.

At the bottom of the loop, it updates and checks the mouse again, and if it's reading zero on either axis, it breaks out of the routine. It's far from perfect. You have to call Display.update alot to keep things moving, whether you're refreshing the display or not. I'm really not satisfied with it yet, but if somebody knows a better way of dealing with a fixed-sized parented GL, I'd love to hear it!



PrintIn3DNow

I'm actually starting to make some decent headway with my crude "Capture the mouse with an underlying canvas" method. My biggest problem is making the canvas wide enough to make sure I capture the 'entered' event. If you move your mouse too fast from the main pane over the undercanvas and into the actual rendering canvas, and the Display.isCreated(), you can lock up the app because nothing is calling the Display.update function.

Does anyone know if there's a Java equivalent for the old VB function DoEvents()?

CodeBunny

...you're syncing the Display update to mouse movement? :-\ Dude. Don't do that.

Disable normal repainting on the Display canvas, then have a separate thread manage update/rendering.

PrintIn3DNow

CodeBunny, I would love to do something else. But I just don't know what the something else is! I did turn off normal repainting for the Canvas, and that's all well and good, but even if I wrote a different thread, what difference would it make if I don't get any events delivered? If I'm using the second thread as a game loop, how do I allow the AWT thread to process messages for the file navigation buttons/lists/etc.?

Is there any way to use the Display class as a _display_, and nothing else? I tried destroy()ing the Mouse., but that doesn't actually seem to do anything but make the isCreated() return false. The Canvas is an event black hole.

CodeBunny

When you place the Display on a Canvas, all that does is allow you to mix the Display with Swing/AWT components. It doesn't automatically do anything to make the Display act as a component itself (nor should it). That said, you can fairly easily integrate things.

Separate the "processing" of your Swing components and the display as much as possible. Don't attempt to render the display from within the event dispatch loop. It's a losing battle and will only make your code less controllable. Instead, create a separate thread exclusive to the Display and its related objects and have that handle your "game loop."

If you want to have your Swing components affect what is happening with the display, you can! Just take advantage of ActionListeners (and similar interfaces). For example, if you want to have an object change color when a button is pressed, make that object an ActionListener and have it update its color whenever an action is performed.

This is how Swing was intended to be used, btw. Separating the thread usage is very important.

PrintIn3DNow

Maybe I need to clarify. I'm totally new to Java, OpenGL, and by extension, LWJGL. I'll go read the tutorial trail on threads. I've seen several examples of creating threads, and maybe I need to just try throwing one on there and see what happens. I suppose I could just sit around and wait for the Display to be created() and see if I can get that to go. You know, I had put in an earlier post, what's the command for the equivalent DoEvents() in VB? I think that's what I'm really missing here. If such a function exists, I could just do everything with a more straight-forward game-loop based design like I see in the examples.

I think my goal is actually modest. I could even post the (ugly ugly) code if you'd like to see it and tinker with it. It's simple really, like any normal CAD or image program. There's navigation on the left side of the Panel, with a drive combo, a directory navigator, a file selection list, and a preview checkbox. Load and Cancel are buttons in a separate panel. In the same panel as the navigation stuff is the Canvas that's set as the Display's parent. Everything is fixed size. (I wanted that to be predictable, at least).

If preview's enabled and you select a file, the display is created, the file is loaded directly into a GL List, and it's rendered onto the canvas. The user can use his mouse to rotate the model and zoom in and out. I have that much working, and would like to add panning as soon as I can figure that out. The biggest problem I have is that once the mouse is over the canvas after display creation, I lose control of the mouse. It's like if I run the game loop, I can't figure out how to let the Swing swing. If I don't run the game loop, so other events can happen, I can't reliably get an event to fire that starts the game loop. It's like they say in the old country, "If you drink the water, you die! If you don't drink the water, you die!"

PrintIn3DNow

OK. So, I made a new Runnable in a separate thread, but that's part of the base JDialog window class. I can get the thread started, and I can do releaseContext from the AWT thread, makeCurrent in the runnable, and  it seems fine. But now there are no mouse positions:

Display.update();
						//System.out.println("Updated Display");
						if ( Mouse.isInsideWindow() )
							// Do all of the stuff
							// This will try to be a game loop to capture mouse events while the canvas is controlled by the Display
							System.out.println("Mouse is in");
						else
							System.out.println("Mouse is out");


I never see the  message that the mouse is in the windows, no matter where the mouse is. There doesn't seem to be a function for Mouse.makeCurrent(), so is there something else I need to do?

Thanks,
Dan B.