Rubber-band zoom

Hi!

I am implementing an application which displays textures using jogl. I would to like to be able to zoom in on the texture using the mouse. The idea is to click once to set the starting point then dragging the mouse will display a rectangle (rubber-band) then the texture is re-displayed only withing the selected rectangle.

  1. does somebody know of an example for using the mouse that way?
  2. do I need to generate a new texture for the zoomed part or can I use the original texture, and if so how?

Many thanks

I found on the web that I could use gluPickMatrix to zoom in on part of the scene.

Currenlty I have:


    public void reshape(GLAutoDrawable drawable,int xstart,int ystart,int width,int height) {
        
        GL gl = drawable.getGL();
        height = (height == 0) ? 1 : height;
               
        gl.glViewport(0, 0, width, height);
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluOrtho2D(0.0, width, 0.0,height);
        // mover the origin from the bottom left corner
        // to the upper left corner
        gl.glScalef(1, -1, 1);
        gl.glTranslatef(0, -height, 0);
          
        // select modelview matrix and clear it out
        gl.glMatrixMode(gl.GL_MODELVIEW);
}

So based on that I have created


    public void zoom(GL gl, int xstart,int ystart,int width,int height) {
        int[] vp = new int[4];       
        //Initialise projection mode,...
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        //...get viewport details,...
        gl.glGetIntegerv(GL.GL_VIEWPORT, vp,0);
        int[] zoomport = new int[4];
        zoomport[0] = xstart;
        zoomport[1] = ystart;
        zoomport[2] = width;
        zoomport[3] = height;
        //Then use pickmatrix to zoom in on this part of the scene...
        glu.gluPickMatrix(zoomport[0]+zoomport[2]/2, zoomport[1]+zoomport[3]/2, zoomport[2], zoomport[3], vp,0);

        glu.gluOrtho2D(xstart, width, ystart,height);
        // mover the origin from the bottom left corner
        // to the upper left corner
        gl.glScalef(1, -1, 1);
        gl.glTranslatef(0, -height, 0);
          
        // select modelview matrix and clear it out
        gl.glMatrixMode(gl.GL_MODELVIEW);

}

the latter function will be called after I have defined the coordonates of the Zoom rectangle with the mouse. Can someone pls tell me if this is a correct way to do this, and what about if I resize the window while in zoom mode?

Have you tried running what you’ve written to see if it’s giving you what you’re after? In many things with OpenGL, there is really no one ‘right’ way to do anything, just common ways, fast ways, slow ways. I don’t know of any ways to do what you’re thinking of though (although an idea of how to do it does come to mind).

Re: the questions in your first post:

  • I can think of a game (Homeworld 2) which focuses (and maybe zooms) to the units you’ve selected in your ‘rubber band’. Dunno if there’s some sort of code or tutorial out there for you to reverse-engineer though.
  • And yes you can use the same texture. Unless you’re changing something drastic between the zoomed and unzoomed versions, I’d highly recommend using the same texture. If you’re after speed optimizations depending upon the various distances from the texture, you could try using mipmaps of the same texture, controlling the quality of the texture using the glTexParameter() function (eg: glTexParameter(GL_TEXTURE_2D, GL_MIN_FILTER, filterlevel) or something like that).

As for window resizing, depends on what you want to do with it. 2 paths come to mind:

  • stick with the same zoomed area
  • keep the current zoom and just expand how much more is visible at that zoom

Once again, depends on what effect you’re after.

Hi,

I personnaly prefer to use picking for picking ;), that is to say to select an object into the world.
What you want is to retrieve coordinates of two points in your world that will be used to narrow the view.
the gluUnProject method do that pretty easily, and even more easily if you are representing your textures in 2D.

But I think that you don’t have to use any OGL specifc method, you can also only use pixel position from the screen and do the math yourself to convert screen coordinates to texture coordinates.

Well that’s just my two cents about it.

I second turquoise’s approach. Picking is not what you want in this case. Just a plain old MouseListener and gluUnProject or your own calculations.

But all he’ll be doing is recreating gluPickMatrix().

My vote is just to use gluPickMatrix, but have some code that ensures that the zoomed region is the same aspect ratio as the canvas

Thanks for your input guys. I am having problems drawing the rubberband on screen. I want to display a rectangle everytime I move the mouse to define the zoom area but nothing displays on top of my existing 2D scene.

Here is what I have implemented:


   public void mousePressed(MouseEvent e) {
        prevMouseX = e.getX();
        prevMouseY = e.getY();
        if ((e.getModifiers() & e.BUTTON2_MASK) != 0) {
            mouseLButtonDown = true;
            GL gl = glc.getGL();
            gl.glEnable(GL.GL_COLOR_LOGIC_OP);
            gl.glLogicOp(GL.GL_XOR);
            
        }
    }

   public void mouseReleased(MouseEvent e) {
        if ((e.getModifiers() & e.BUTTON2_MASK) != 0) {
            mouseLButtonDown = false;
            GL gl = glc.getGL();
            gl.glDisable(GL.GL_COLOR_LOGIC_OP);
            //erase rectangle
            drawRect(gl,prevMouseX,prevMouseY,e.getX(),e.getY());
        }
    }

   public void mouseDragged(MouseEvent e) {
        int x = e.getX();
        int y = e.getY();
        GL gl = glc.getGL();
        // display rectangle
        drawRect(gl,prevMouseX,prevMouseY,x,y);
        glc.swapBuffers(); 
    }
with

   private void drawRect(GL gl, int x1, int y1, int x2, int y2) {
        gl.glColor3f(1.0f,1.0f,1.0f);
        gl.glBegin(GL.GL_LINE_LOOP);
        gl.glVertex2f(x1, y1);
        gl.glVertex2f(x2, y1);
        gl.glVertex2f(x2, y2);
        gl.glVertex2f(x1, y2);
        gl.glEnd();
    }

[I draw everytihing using an orthographic projection (see my first post) so I dont need to change that. What have I missed? In the debugger drawRect is called with the correct values…

I guess you should only update some global variables (e.g. isMousePressed, mouseX, mouseY from within the mouse-input methods and do the real drawing from within display()

In addition, for the best accuracy, you could synchronize mousePressed(), mouseReleased() and mouseDragged() as well as your display() method

This. OpenGL should only be issued from inside your init(), reshape() or display() methods, or they won’t have any effect. I think I remember reading that using the DebugGL pipeline will throw an exception if you break this rule, but I haven’t seen it in action myself.

This is true. I dunno if these are the right words, but the GL pipeline of the OpenGL context, must be made current on the currently executing thread, otherwise your GL commands will not work.

If you’re using JOGL’s GLEventListener and Animator classes, then this is automatically done for you by the time the thread of execution reaches display(), init(), or reshape(). Event listeners set-up to receive OS-message input, such as keyboard presses or mouse movement events, are a different thread of execution from that which runs your OpenGL rendering thread.

You could try making an OpenGL context current when those mouse events are run (GLContext.makeCurrent()), although I’m not sure if this will work. Alternatively, devise some way of passing/storing mouse event requests which can be serviced by calls from the display(), init(), or reshape() methods.

Hi Guys,

Thanks for the suggestions now the rubber-band works fine with the mouse. Evrything is done in display() now. My problem is now the zoom function itself I need to perform based on the zoom selection rectangle. With the function given in my original post (called zoom) it does not work (i.e. does not zoom properly). Can somebody give me some details on how to proceed, using gluPickMatrix or not

Many thanks!

Hi, I don’t know which way you’re using to display your image but if you use a textured quad I think you just have to change the texture coordinates and keep the quad the size of your screen.
The other way is to change the viewing region using gluOrtho2D (for instance). I’m a little bit too busy to look at your method in details… so I can’t tell you what’s wrong in it…

Hope it helps nevertheless.

I am not only displaying an image but also drawing some wiggles on top of it (this is seismic data).

I have traceWCInc, an increment between each trace (X directtion) and sampWCInc between each sample (Y direction) thus I end up drawing the following size on screen:

From traceWCStart ,sampWCStart (0,0)
to
traceWCEnd = traceWCStart + ntracestraceWCInc;
sampWCEnd = sampWCStart + nsamp
sampWCInc;