Causing the stuff to refresh

Hi,

I’m beginning to feel a little guilty about making so many posts, but anyway…

If something changes among my 3d objects and I basically need to tell the system to refresh the view, what’s the right way to do it?

I basically call GLCanvas.display() but something tells me that is not the right way to do it.

What is?

Thanks a lot for the help I’m getting…

Pahidla

Simply put, GLCanvas.display() is the
recommended way to do things.

May I suggest that you go thru the
tutorials by Gregory Pierce posted in
this forum. It covers a lot of basic things.

Also, going thru the demo sources
bundled with JOGL will show you the
proper way of doing things.

Could someone be kind enough to modify Gregory Pierce’s to show how to write a simple “painting on demand” application.

I’ve read the existing tutorial very carefully, but I can’t quite figure out how kick things off in the “on demand” mode.

When I call GLCanvas.display() from a button callback I find that all GL commands take place in the “AWT-EventQueue-0” tread which I understand is incorrect. How can that be fixed? (I tried using SwingUtilities.invokeLater but that doesn’t seem to help…)

(I already have what I think is a very cool application which mimics the Examiner, I think it was called, in Magician where viewing parameters can be continuously adjusted, etc. Everything seems to work - albeit incorrectly from the threads point of view.)

I’ll post you a simple example tomorrow morning (Finnish time that is [GMT+2] :slight_smile: )

Thank you. You’re a mench.

Ok, I’ll try to explain JOGL and threads in a clear way. Generally, there are two ways in which you can use JOGL to render graphics:

  • On-demand -> the GLDrawable (GLCanvas, etc.) is only refreshed when the repaint/display is called by AWT (component resized, etc.) or by the user.

  • Continuous -> the GLDrawable is continuously refreshed by another thread (Animator class, etc.).

The possible problems with threads arise when two different threads try to operate on the same OpenGL context.

In JOGL terms, this basically means that if you’re rendering the GLDrawable with an Animator (or some other implementation involving multithreading) AND try to invoke OpenGL operations on the same GLDrawable from another thread, problems will occur.

However, if you’re only refreshing the GLDrawable manually (no threading involved), it’s OK to make OpenGL calls from the GUI thread, since no other thread is using the GLDrawable and its OpenGL context anyway.

So pahidla, you’re doing nothing wrong. :slight_smile:

Thank you for your vote on confidence and, more importantly, for taking the time the help the less advanced users. Your posting was extremely insightful and educational.

That said, I am doing something wrong. I’m having many problems, so I’ll take them one at a time.

I’m operating in the “on-demand” mode. But I have a problem kicking things off. When the JFrame containing my GLCanvas comes up, the init() or display() are not called. In order to trigger them, I have to manually resize the window and then everything gets going.

So how do I fix this?

[quote]I’m operating in the “on-demand” mode. But I have a problem kicking things off. When the JFrame containing my GLCanvas comes up, the init() or display() are not called. In order to trigger them, I have to manually resize the window and then everything gets going.
[/quote]
Thank you for your kind words.
Can you post your initialization code here (Frame/GLCanvas creation, etc.), so we can take a look at it?

Thank you!

Here it is, maximally compressed. With a little bit of work I will be able to post something that actually runs. Or, I could include a small flash presentation of what happens on the screen. This is a JPanel which is later included in a JFrame.

package laba;

import pmgjutils.Value;
import pmgjutils.ValuePanel;

import javax.swing.;
import java.util.
;
import java.awt.;
import java.awt.event.
;

import net.java.games.jogl.*;

public class ImaxToPost extends JPanel {
private GLCanvas myGLCanvas = GLDrawableFactory.getFactory().createGLCanvas(new GLCapabilities());

private ArrayList myContents = new ArrayList();
private ArrayList my3DObjects = new ArrayList(); // Basically an ArrayList of ints representing gl lists.

/*
Value.Double is basically a Double which triggers a callback when changed. Value.DoubleWheelable is a similar thing except it creates a
a little gui component that looks like a wheel. Value.Boolean is the same for boolean except it creates a checkbox Etc…
*/
private Value.DoubleWheelable myTheta = new Value.DoubleWheelable(“Longitudal Angle, \u03b8 (in units of \u03c0)”, .5, 0.01, 1);
private Value.DoubleWheelable myPhi = new Value.DoubleWheelable(“Azimuthal Angle, \u03c6 (in units of \u03c0)”, -.5, 0.01, 0);
private Value.DoubleWheelable myZoom = new Value.DoubleWheelable(“Zoom”, 2.0, 0.1, 1);
private Value.Boolean myPerspectivePresent = new Value.Boolean(“Perspective?”, false);
private Value.DoubleRange myPersp = new Value.DoubleRange(“View Angle”, Math.PI/3, 0, Math.PI);

public GLCanvas pGetCanvas3D() { return myGLCanvas; }

private static void gRefreshCanvas(GLCanvas inCanvas) {
// Experiments with threads. Basically calles myGLCanvas.display();
final GLCanvas canvas = inCanvas;
// SwingUtilities.invokeLater(new Runnable() {
// public void run() {
// System.out.println("About the give the repaint command! " + Thread.currentThread().getName());
canvas.display();
// }
// });
}

public ImaxToPost() {
setLayout(new BorderLayout());

Value[] values = new Value[] {
    myZoom, myTheta, myPhi, myPerspectivePresent, myPersp,
    Value.SEPARATOR, Value.SEPARATOR };

for (int v = 0; v < values.length; v++) { // refresh when a view paramter changed.
  values[v].pAddValueListener(new Value.Listener() {
    public void pOnAction(Value.Event inE) { gRefreshCanvas(myGLCanvas); }
  });
}

InteractionListeners il = new InteractionListeners();
myGLCanvas.addComponentListener(il);
myGLCanvas.addMouseListener(il);
myGLCanvas.addMouseMotionListener(il);

}

public void pKickStart() {
myGLCanvas.addGLEventListener(new OnScreenGLEventListener());
add(myGLCanvas, BorderLayout.CENTER);
}

public class OnScreenGLEventListener implements GLEventListener {
public void init(GLDrawable inDrawable) {
System.out.println("Init is being called by " + Thread.currentThread().getName());
GL gl = inDrawable.getGL();

  gl.glClearColor(1f, 1f, 1f, 1f);

  gl.glEnable(GL.GL_DEPTH_TEST);
  gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
  gl.glEnable(GL.GL_POLYGON_OFFSET_LINE);

  // Lights
  gl.glEnable(GL.GL_LIGHTING);
  gl.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, new float[] { 1f, 1f, 1f, 1f }); // was .6 .6 .6 1

  gl.glDisable(GL.GL_CULL_FACE) ;
  gl.glLightModeli(GL.GL_LIGHT_MODEL_TWO_SIDE, GL.GL_TRUE) ;
}

public void display(GLDrawable drawable, boolean modeChanged, boolean deviceChanged) {}
public void displayChanged(GLDrawable inDrawable, boolean inWhat, boolean inWhen) {}
public void reshape(GLDrawable drawable, int x, int y, int width, int height) {}

public void display(GLDrawable inDrawable) {

// System.err.println(Thread.currentThread().getName());
// try { throw new RuntimeException(); } catch (Exception inE) { inE.printStackTrace(System.out); }
GL gl = inDrawable.getGL();
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

  pSetView(inDrawable, false);

  for (int i = 0; i < myContents.size(); i++) {
    Buildable buildable = (Buildable) myContents.get(i); // Definition of Buildable below
    if (buildable.pHasBeenTouched()) {
      Object obj = buildable.pGenerate3D(inDrawable, null);
      if (my3DObjects.size() > i && my3DObjects.get(i) != null)
        my3DObjects.set(i, obj);
      else
        my3DObjects.add(i, obj);
      buildable.pUntouch();
    }

    if (buildable.pIsVisible() && ((Integer) my3DObjects.get(i)).intValue() != 0)
      gl.glCallList(((Integer) my3DObjects.get(i)).intValue()); // Reveals implementation
  }

  gl.glFlush();

}

}

/*

public interface Buildable {
public abstract Object pGenerate3D(net.java.games.jogl.GLDrawable drawable, Object inOther);
public abstract String pGetName();
public abstract boolean pIsVisible();
public boolean pHasBeenTouched();
public Buildable pTouch();
public Buildable pUntouch();
}

*/
public void pSetView(GLDrawable inDrawable, boolean inFlip) {
pSetView( (double) myGLCanvas.getSize().width / myGLCanvas.getSize().height, inDrawable, inFlip);
}

public void pSetView(double inAspect, GLDrawable inDrawable, boolean inFlip) {
GL gl = inDrawable.getGL();
GLU glu = inDrawable.getGLU();

gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();

double dim = myZoom.pValue();
if (myPerspectivePresent.pValue()) {
  glu.gluPerspective(myPersp.pValue()*180/Math.PI, inAspect, .1, 100000.0);
  dim = myZoom.pValue() / Math.tan(myPersp.pValue() / 2);
}
else
  gl.glOrtho(-myZoom.pValue() * inAspect, myZoom.pValue() * inAspect, -myZoom.pValue(), myZoom.pValue(), -10.0, 100.0);

gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
double theta = myTheta.pValue() * Math.PI;
double phi = myPhi.pValue() * Math.PI;
double upOrDown = 1 - 2 * (int) (pmgjutils.math.PmgMath.gMod(theta, 2 * Math.PI) / Math.PI);
glu.gluLookAt(dim * Math.sin(theta) * Math.cos(phi),
              dim * Math.sin(theta) * Math.sin(phi), dim * Math.cos(theta),
              0.0, 0.0, 0.0,
              0.0, 0.0, (!inFlip)?upOrDown:-upOrDown);

}

private class InteractionListeners implements MouseListener, MouseMotionListener, ComponentListener {
private int myLastX, myLastY;
public void mousePressed(MouseEvent inME) {
if (inME.getButton() == MouseEvent.BUTTON1) {
myLastX = inME.getX();
myLastY = inME.getY();
}
else if (inME.getButton() == MouseEvent.BUTTON3) {
int x0 = (int) ((GLCanvas) inME.getSource()).getParent().getBounds().getX();
int y0 = (int) ((GLCanvas) inME.getSource()).getParent().getBounds().getY();
int x = inME.getX(), y = inME.getY();
}
}

public void mouseDragged(MouseEvent inME) {
  if ((inME.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0) return;

  if ((inME.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0 ) {
    myZoom.pIncrement((inME.getY() - myLastY)/30.0/Math.PI);
  }
  else if ((inME.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0 ) {
    myPersp.pIncrement(-(inME.getY() - myLastY)/600.0/Math.PI);
  }
  else {
    myPhi.pIncrement(  -(inME.getX() - myLastX)/300.0/Math.PI);
    myTheta.pIncrement(-(inME.getY() - myLastY)/300.0/Math.PI);
  }
  myLastX = inME.getX();
  myLastY = inME.getY();
}

public void mouseMoved(MouseEvent inME) { }
public void mouseReleased(MouseEvent e) { }
public void mouseClicked(MouseEvent inME) {
  if (inME.getButton() == MouseEvent.BUTTON1 && inME.getClickCount() == 2) {
    myPerspectivePresent.pNegate();
    return;
  }

  final MouseEvent e = inME;
  SwingUtilities.invokeLater(new Runnable() {
    public void run() {
      int x = e.getX(), y = e.getY();

      GL gl = myGLCanvas.getGL();

      char[] l = new char[3];
      gl.glReadPixels(x, y, 3, 3, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, l);
      System.out.println(x + " " + y + " " + (int) l[0] + " " + (int) l[1] + " " + (int) l[2]);
    }
  });
}
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void componentHidden(ComponentEvent inCE) { }
public void componentShown(ComponentEvent inCE) { }
public void componentMoved(ComponentEvent inCE) { }
public void componentResized(ComponentEvent inCE) { }

}
}

By the way, I welcome ALL comments about my code - style, observations, criticisms, etc!

It isn’t valid to do the glReadPixels call in your SwingUtilities.invokeLater() block. With the current JOGL APIs, you must perform all OpenGL calls from within your init() or display() routines, which is when the OpenGL context is current. In order to cause the mouse event to have the effect it does, put it on a queue or set some other instance variable that will cause that code to be executed the next time display() is called.

Now we are getting to the bottom of my confusion!

So how about something like:

  myPixels = new long[myGLCanvas.getWidth()*myGLCanvas.getHeight()];
  gl.glReadPixels(0, 0, myGLCanvas.getWidth(), myGLCanvas.getHeight(), GL.GL_RGB, GL.GL_UNSIGNED_BYTE, myPixels);

in display() and the refer to myPixels in the click callback?

I think that in your application you probably want to only do the glReadPixels upon getting a mouse click. I would fetch the coordinates out of the mouse event in your EventListener and do the glReadPixels call and subsequent inspection of the pixel value in your display() method rather than trying to do all of the work in your EventListener’s method.

Ken, that’s not what I meant. What I was trying to imply is that in every display() I will grab the entire image (in put it in myPixels) and the just lookup the value in myPixels[] upon every click. (Sounds like a good plan to me!) The application is such that clicks to query the pixels will follow almost every display().

BTW, I’m confused about the very glReadPixels command. I’ve looked up the c-spec and there’s too much info. Did I basically give a correct command, giving it an array of longs of length HxW and passing the parameters GL.GL_RGB, GL.GL_UNSIGNED_BYTE?

OK, maybe I misunderstood. glReadPixels is an expensive operation and if your application is animating continuously then you definitely don’t want to call it every frame because it will slow the app down. However if you’re only repainting infrequently then your approach sounds fine.

You probably want to pass in a byte[] of the given dimensions (HxWx(num components, i.e. 4)) with the flags you’ve specified. That way all components can be accessed directly out of the array without masks and shifts. Check the NeHe web site; there is probably a tutorial that shows how to use glReadPixels (Lesson 44?)

pahidla,

I see that you are using glReadPixels() so I feel
I must warn you about its performance. Assuming
that there are no pixel transfer modes active,
the bus (AGP or PCI-E) will affect your read speeds
greatly. AGP is notorious for ‘read-backs’ and
many of us using OpenGL for video processing
have a lot of pain dealing with it. On a good day
you can get ~12-15 DV frames per second.
PCI-E is totally different, giving 30-40 DV frames
per second on first-gen cards and drivers.
AGP is half-duplex and asymmetrical and PCI-E
is the total opposite.

Hope this will be helpful.

.rex

Hi Rex,

It did help, even though I didn’t understand most of the terms you used buy I’ve gathered that I should stay away from it.

Actually, I only use it on mouse release after mouse drag which for my application basically means nothing else is hapenning.

Just out of curiousity, can’t java (rather than opengl) tell me the color of a pixel on the screen?

Pahidla

It’s OK to use it if you’re reading very few pixels,
just be careful if you’re reading the whole screen
and expect it to be fast.

Well unfortunately Java can’t tell you the color,
because the surface that JOGL uses is pretty
much owned by OpenGL.

.rex

Pahidla>Just out of curiousity, can’t java (rather than opengl) tell me the color of a pixel on the screen?<

Java does provide a method for reading the color of a pixel on a screen, the getPixelColor(int x, int y) method of the java.awt.Robot class. But it may not be what you want. For one thing, x and y are absolute screen coordinates, whereas you mouse listener will be returning coords relative to your Canvas. So you have to convert between the two. Second, it returns the color on the screen, where your mouse clicked. Which is usually the mouse cursor block color. Not what you want. So you need to use the Robot class to move the mouse out of the way first, then pick up the color, then return the cursor. Third, I have heard rumor this Robot method doesn’t work so well on Macs, but haven’t tried it as yet myself. Picking the color OpenGL thinks is on the screen, through glReadPixels(), sounds like a safer bet for your application if you can get it to work. In my application I used the color that actually is on the screen. That turned out to be more complex than I expected.

Thank you. Very useful information.