Search code examples
androidscrollopengl-espanning

Android. OpenGl ES. panning/scrolling


I'm writing an app which displays map. User can zoom and pan. Map is rotated according to magnetometer's value (map is rotated in opposite direction of device rotation).

For scaling I'm using ScaleGestureDetector and passing scale factor to Matrix.scaleM.

For panning I'm using this code:

GlSurfaceView side:

private void handlePanAndZoom(MotionEvent event) {
    int action = MotionEventCompat.getActionMasked(event);
    // Get the index of the pointer associated with the action.
    int index = MotionEventCompat.getActionIndex(event);
    int xPos = (int) MotionEventCompat.getX(event, index);
    int yPos = (int) MotionEventCompat.getY(event, index);

    mScaleDetector.onTouchEvent(event);

    switch (action) {
        case MotionEvent.ACTION_DOWN:
            mRenderer.handleStartPan(xPos, yPos);
            break;
        case MotionEvent.ACTION_MOVE:
            if (!mScaleDetector.isInProgress()) {
                mRenderer.handlePan(xPos, yPos);
            }
            break;
    }
}

Renderer side:

private static final PointF mPanStart = new PointF();
public void handleStartPan(final int x, final int y) {
    runOnGlThread(new Runnable() {
        @Override
        public void run() {
            windowToWorld(x, y, mPanStart);
        }
    });
}

private static final PointF mCurrentPan = new PointF();
public void handlePan(final int x, final int y) {
    runOnGlThread(new Runnable() {
        @Override
        public void run() {
            windowToWorld(x, y, mCurrentPan);
            float dx = mCurrentPan.x - mPanStart.x;
            float dy = mCurrentPan.y - mPanStart.y;
            mOffsetX += dx;
            mOffsetY += dy;
            updateModelMatrix();
            mPanStart.set(mCurrentPan);
        }
    });
}

windowToWorld function uses gluUnProject and works because I'm using it for many other tasks. UpdateModelMatrix:

private void updateModelMatrix() {
    Matrix.setIdentityM(mScaleMatrix,0);
    Matrix.scaleM(mScaleMatrix, 0, mScale, mScale, mScale);

    Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, 1.0f);

    Matrix.setIdentityM(mTranslationMatrix,0);
    Matrix.translateM(mTranslationMatrix, 0, mOffsetX, mOffsetY, 0);

    // Model = Scale * Rotate * Translate
    Matrix.multiplyMM(mIntermediateMatrix, 0, mScaleMatrix, 0, mRotationMatrix, 0);
    Matrix.multiplyMM(mModelMatrix, 0, mIntermediateMatrix, 0, mTranslationMatrix, 0);
}

Same mModelMatrix is used in gluUnproject of windowToWorld function for point translation.

So my problem two-fold:

  1. The panning occurs twice slower than the movement of finger on the device's screen
  2. At some point when panning continuously for a few seconds (making circles on screen, for example) the map starts to 'shake'. The amplitude of this shaking is getting bigger and bigger. Looks like some value adds up in handlePan iterations and causes this effect.

Any idea why these happen?

Thank you in advance, Greg.


Solution

  • Well, the problem with my code is this line:

            mPanStart.set(mCurrentPan);
    

    Simply because I drag in world coordinates, and update offset, but current location stays the same. This was my bug.

    Removing this line will fix everything.