I’m trying to implement a pan and pinch-to-zoom feature into an OpenGL application I have. I was able to implement the pan feature relatively easily with a simple glTranslatef, but using the glScalef function is presenting much more difficulty. I have three classes, one for the GLSurfaceView, one for the GLTriangle, and the third (below) for the GLRenderer, which is where the glScalef and glTranslatef are used.
GLRenderer Class:
public class GLRendererEx implements Renderer, OnTouchListener {
private GLTriangleEx tri;
float ratio;
float x = 0, y = 0;
float dx = 0, dy = 0;
float sx = 0, sy = 0;
float tx = 0, ty = 0;
float xtwo = 0, ytwo = 0;
float sxtwo = 0, sytwo = 0;
float screenX, screenY;
float xscale = 1, yscale = 1;
float xscaletwo = 1;
float xscaletotal = 1, yscaletotal = 1;
int NONE = 0, DRAG = 1, ZOOM = 2;
int mode = NONE;
int width, height;
boolean touched = true;
Display getOrient = getWindowManager().getDefaultDisplay();
int orientation;
public GLRendererEx() {
tri = new GLTriangleEx();
screenX = ourSurface.getWidth();
screenY = ourSurface.getHeight();
ourSurface.setOnTouchListener(this);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig) {
gl.glDisable(GL10.GL_DITHER);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
gl.glClearColor(0f, 0f, 0f, 1f);
gl.glClearDepthf(1f);
orientation = getOrient.getRotation();
}
@Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl, 0, 0, -5, 0, 0, 0, 0, 2, 0);
//warning - x is backwards
gl.glTranslatef(tx, ty, 0);
gl.glScalef(xscaletotal, yscaletotal, 1);
tri.draw(gl);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
ratio = ((float) width) / height;
screenX = width;
screenY = height;
orientation = getOrient.getRotation();
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 50);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
event.getActionIndex();
sx = x = event.getX(0);
sy = y = event.getY(0);
mode = DRAG;
break;
}
case MotionEvent.ACTION_POINTER_DOWN: {
event.getActionIndex();
sxtwo = xtwo = event.getX(1);
sytwo = ytwo = event.getY(1);
mode = ZOOM;
touched = true;
break;
}
case MotionEvent.ACTION_MOVE: {
x = event.getX(0);
y = event.getY(0);
xtwo = event.getX(1);
ytwo = event.getY(1);
if (mode == ZOOM)
midPoint(x, y, xtwo, ytwo);
if (mode == DRAG) {
dx = x - sx;
dy = y - sy;
dx *= -8;
dy *= -8;
if (orientation == Configuration.ORIENTATION_LANDSCAPE)
dx *= 2;
dx /= screenX;
dy /= screenY;
tx += dx;
ty += dy;
sx = x;
sy = y;
}
break;
}
case MotionEvent.ACTION_POINTER_UP: {
mode = DRAG;
break;
}
case MotionEvent.ACTION_UP: {
mode = NONE;
break;
}
}
return true;
}
private float midPoint(float x, float y, float xtwo, float ytwo) {
x -= xtwo;
y -= ytwo;
if (touched) {
sxtwo = x;
sytwo = y;
xscaletwo = FloatMath.sqrt(sxtwo * sxtwo + sytwo * sytwo);
touched = false;
}
xscale -= xscaletwo;
xscale = FloatMath.sqrt(x * x + y * y);
xscaletotal += xscale;
yscaletotal = xscaletotal;
return xscale;
}
}
x/y‘s are coordinates, dx/dy are the change in the coords, sx/sy‘s are starting coords, tx/ty are the total changes in coords (for panning), xscale/yscale‘s are the floats used to calculate the scale for zooming, xscaletotal/yscaletotal are total changes in zoom scale, none drag zoom and mode represent the mode.
As it stands, I can zoom in by pulling my fingers apart, but a pinch also zooms instead of zooming out. Also, the zoom is much too sensitive.
I don’t really know how to solve this problem from here. Any help would be more than appreciated.
It would seem your “midPoint” has some work to do.. Try doing something like this: