how to zoom smoothly

I’m having a problem trying to zoom in and out of my models smoothly. What I have below works, but the more I zoom in (or the larger it gets) … the bigger the jump it makes to the next zoom level and the model doesn’t appear to “grow” proportionately or smoothly. Pressing the + or - key makes big jumps on the size of the model.

So I’m wondering what better ways to do this would be. Any help much appreciated.

I use glOrtho to zoom in and out of my model.


public void display(GLAutoDrawable drawable) {
   GL gl = drawable.getGL();

   ...

   gl.glOrtho(-winWidth * getScaleFactor(), winWidth * getScaleFactor(), -winHeight * getScaleFactor(), winHeight * getScaleFactor(), 100, -100);

   ...
}

Here is the code where I set the zoom value and depending on the model I display, the 0.005 value doesn’t work to well.


public void keyPressed(KeyEvent e) {
   switch (e.getKeyCode()) {
      case KeyEvent.VK_EQUALS:
         glListener.setScaleFactor(glListener.getScaleFactor() - 0.005f);
         break;

      case KeyEvent.VK_MINUS:
         glListener.setScaleFactor(glListener.getScaleFactor() + 0.005f);
         break;
   }
   
   this.glListener.glDrawable.display();

   return;
}

the entire zoom is done by a call of “keyPressed”… of course this is jumpy.
you shoult start an animator in “keyPressed” an interpolate a zoom range in the display method. so you need the elapsed time between two frames, a current zoom factor and the destination zoom factor.
if you have 25 display call within a second you can get smooth zooming this way.

I looked through my opengl redbook and there’s nothing in there about animators, sort of. There’s animating, or using double-buffering, but I don’t think it’s what your talking about. Where’s a good link to start w/ this?

there is nothing about animators in the redbook, because its jogl stuff. an animator calls your display method until you call animator.stop(). have a look at these two jogl chapters (http://fivedots.coe.psu.ac.th/~ad/jg/) from Andrew Davison or at the docs (http://download.java.net/media/jogl/builds/nightly/javadoc_public/). it should be obvious that you need multiple frames (calls to the display method), to get something moved or zoomed smoothly.

Ok, I looked at the animator and I’ve already got the same functionality as this, only much easier than an animator. If the key is pressed … keyPressed(KeyEvent e) … then at the end of my keyPressed() I make from my KeyAdapter a call to glListener.getGLDrawable().display(). Which is all the animator does if I understand it correctly.

So all I really need to figure out is how to generate and interpolate the values correctly into my glOrtho call (which will zoom in or out my model), because even if I use an animator, I’d still have to do the same thing, and using an animator, the smoothness will still depend on the values I feed into glOrtho for my zooms. Correct?

Also, another reason I think I shouldn’t use Animator is because I don’t have a destination zoom factor because I only want to zoom while the key is pressed, so I really don’t know the “final” zoom factor till the user lets up off the zoom key.

Thoughts/ideas?

Here’s a different take on the Animator. The animator runs concurrently with the rest of your program, so the opengl context redraws as often as possible (or as close to whatever desired framerate you ask for as it can). What your code is doing is a rotation every time a key is pressed, and even if you press an hold a key down, I hope you don’t have your key repeat speed so high as, say, 60 time per second ( a really nice, smooth framerate).

What you need to do is, in your keyPressed even, simply set a flag that says that key was pressed (keyFPressed = true; or some such statement). Then, add the animator, and in your display(GLAutoDrawable g) method, figure out how much time has passed since the LAST time you updated the screen. I do this by having a process method which reads in the System.nanoTime(); of System.currentTimeMillis();, subtracts a class variable (oldTime) from that to get the amount of time that’s passed, multiply that by the appropriate small value (0.001 for currentTimeMillis() and something much smaller for nanoTime()) to get the ration of how much of a second has passed, and then process all of my moving objects by where they’re trying to move/rotate to, and how fast they can move per second, and the retio of how much of a second has passed, then move them that much each frame.

This method is guaranteed to get you a nice, smooth rotation of your object.

Of course, if anyone has any other suggestions, please post them, as I’d hate to think my way is the only way of doing things. I’m also always interested in finding out if there’s a better way than what I’m currently doing! ^^;;

–Scott

hmm…
try

e.getComponent().repaint();

to redraw your scene by another thread… this will not block and there is more time to process other key-events… maybe it helps.

[quote=“Lareon,post:6,topic:26569”]
I think I understand a bit more about the Animator and how it will smooth out my zooms, but I don’t understand where to use it.

This is my display() of my GLEventListener class.


...
    gl.glOrtho(-winWidth * getScaleFactor(), winWidth * getScaleFactor(), -winHeight * getScaleFactor(), winHeight * getScaleFactor(), 100, -100);
    // Draw the model.
    {
	gl.glMatrixMode(GL.GL_MODELVIEW);
	drawModel(drawable);
    }

    lastTime = currentTime;
    currentTime = System.nanoTime();
    elapsedTime = currentTime - lastTime;

    if (keyHandler.IsZoomPressed()) {
        setScaleFactor((float) (elapsedTime * 0.00000001));
    }

And this is my KeyAdapter.


// my constructor
public KeyInputHandler(MyRenderer listener) { 	
    glListener = listener;
    animator = new Animator(glListener.getGLDrawable());
}

public void keyPressed(KeyEvent e) {
    switch(e.getKeyCode() {
        case KeyEvent.VK_EQUALS:
            // animator.start();
            zoomInPressed = true;
...	

public void keyReleased(KeyEvent e) {
    switch(e.getKeyCode() {
        case KeyEvent.VK_EQUALS:
            // animator.stop();
            zoomInPressed = false;
...
public boolean IsZoomPressed() {
    return zoomInPressed || zoomOutPressed ? true : false;
}

Of course my zooming isn’t working w/ what I have above. Here’s what I understand.

  1. Make call to display() to get current time (using System.nanoTime()).
  2. Next call to display() gets new current time and save last current time as old time.
  3. Substract current time from last time to get elapsed time (time since last frame displayed).
  4. Multiply elapsed time by small value ( 0.00000001for nanoseconds) to get the scale factor of which my model will be zoomed.

But this doesn’t seem to be right.

Also, I called the start method as soon as my canvas was created and my cpu usage is already at 100% just showing an empty canvas (before I even load my model). So I figure I’m not doing something right.


    glCanvas = new GLCanvas();
    animator = new Animator(glCanvas);
    animator.start()

What I’ve listed is what I understand, but I don’t understand how to implement the animator by doing this. If I start the animtor everytime I press the zoom key, I think I’m starting new threads w/o stopping them and just overloading the cpu not to mention all sorts of other havoc I don’t know about. So I’m really not sure on where and how to start and stop the animator for each zoom key that is tapped.

Any help much appreciated.

this seams be the right way, but you should add the new scaleFactor the old one.

setScaleFactor((float) (elapsedTime * 0.00000001));
replace by
setScaleFactor((float) (getScaleFactor() + (elapsedTime * 0.00000001)));

also you should make sure, not to start the animator twice (by zooming in and out at the same time).
the 100% cpu load is ok, because the empty canvas rendering is not bound to your graphics card and the animator is not limitet to a number maximum fps. so glClear and swapBuffers are called all the time.

Well, it certainly moves smoother than before, has a little jerk once the key goes down, but at least it’s better and usuable. Eventually I’ll have to find a better way to manage my camera and make all this “extremely” smooth, but this work.

Am I supposed to start it when the key is pressed? And stop it when the key is released? I started the Animator as soon as my program started so it’s always running.

I have the concerns of this thread now running in the background. I have an arcball rotation control implementation that really rotates much jerkier now because it seems like the cpu is so busy at 100%.

Here’s what I ended up with.


private void updateTime() {
    if (lastTime == -1)  {
        lastTime = System.currentTimeMillis();
        lastFrameTime = 0;
    }
    else  {
        long currentTime = System.currentTimeMillis();
        lastFrameTime = currentTime - lastTime;
        lastTime = currentTime;
    }
    
    if (keyHandler.isZoomInPressed()) {
	timeRatio = (float) (lastFrameTime * -0.0001);
	setScaleFactor((float) (getScaleFactor() + timeRatio));
    }
    else if (keyHandler.isZoomOutPressed()) {
	timeRatio = (float) (lastFrameTime * 0.0001);
	setScaleFactor((float) (getScaleFactor() + timeRatio));
    }
}