- fixed MapViewPosition scaleMap

- fixed GLRender isVisible() for proxy tiles
- smoother scale animation
This commit is contained in:
Hannes Janetzek 2012-09-22 07:24:35 +02:00
parent 8d57bc7531
commit 7d7cf10d89
13 changed files with 511 additions and 410 deletions

View File

@ -44,10 +44,12 @@ public final class MercatorProjection {
public static final double LONGITUDE_MIN = -LONGITUDE_MAX; public static final double LONGITUDE_MIN = -LONGITUDE_MAX;
/** /**
* Calculates the distance on the ground that is represented by a single pixel on the map. * Calculates the distance on the ground that is represented by a single
* pixel on the map.
* *
* @param latitude * @param latitude
* the latitude coordinate at which the resolution should be calculated. * the latitude coordinate at which the resolution should be
* calculated.
* @param zoomLevel * @param zoomLevel
* the zoom level at which the resolution should be calculated. * the zoom level at which the resolution should be calculated.
* @return the ground resolution at the given latitude and zoom level. * @return the ground resolution at the given latitude and zoom level.
@ -58,7 +60,8 @@ public final class MercatorProjection {
} }
/** /**
* Converts a latitude coordinate (in degrees) to a pixel Y coordinate at a certain zoom level. * Converts a latitude coordinate (in degrees) to a pixel Y coordinate at a
* certain zoom level.
* *
* @param latitude * @param latitude
* the latitude coordinate that should be converted. * the latitude coordinate that should be converted.
@ -79,7 +82,8 @@ public final class MercatorProjection {
} }
/** /**
* Converts a latitude coordinate (in degrees) to a tile Y number at a certain zoom level. * Converts a latitude coordinate (in degrees) to a tile Y number at a
* certain zoom level.
* *
* @param latitude * @param latitude
* the latitude coordinate that should be converted. * the latitude coordinate that should be converted.
@ -103,7 +107,8 @@ public final class MercatorProjection {
/** /**
* @param longitude * @param longitude
* the longitude value which should be checked. * the longitude value which should be checked.
* @return the given longitude value, limited to the possible longitude range. * @return the given longitude value, limited to the possible longitude
* range.
*/ */
public static double limitLongitude(double longitude) { public static double limitLongitude(double longitude) {
return Math.max(Math.min(longitude, LONGITUDE_MAX), LONGITUDE_MIN); return Math.max(Math.min(longitude, LONGITUDE_MAX), LONGITUDE_MIN);
@ -120,7 +125,8 @@ public final class MercatorProjection {
} }
/** /**
* Converts a longitude coordinate (in degrees) to a pixel X coordinate at a certain zoom level. * Converts a longitude coordinate (in degrees) to a pixel X coordinate at a
* certain zoom level.
* *
* @param longitude * @param longitude
* the longitude coordinate that should be converted. * the longitude coordinate that should be converted.
@ -138,7 +144,8 @@ public final class MercatorProjection {
} }
/** /**
* Converts a longitude coordinate (in degrees) to the tile X number at a certain zoom level. * Converts a longitude coordinate (in degrees) to the tile X number at a
* certain zoom level.
* *
* @param longitude * @param longitude
* the longitude coordinate that should be converted. * the longitude coordinate that should be converted.
@ -151,7 +158,8 @@ public final class MercatorProjection {
} }
/** /**
* Converts a pixel X coordinate at a certain zoom level to a longitude coordinate. * Converts a pixel X coordinate at a certain zoom level to a longitude
* coordinate.
* *
* @param pixelX * @param pixelX
* the pixel X coordinate that should be converted. * the pixel X coordinate that should be converted.
@ -178,7 +186,8 @@ public final class MercatorProjection {
} }
/** /**
* Converts a pixel Y coordinate at a certain zoom level to a latitude coordinate. * Converts a pixel Y coordinate at a certain zoom level to a latitude
* coordinate.
* *
* @param pixelY * @param pixelY
* the pixel Y coordinate that should be converted. * the pixel Y coordinate that should be converted.
@ -206,7 +215,8 @@ public final class MercatorProjection {
} }
/** /**
* Converts a tile X number at a certain zoom level to a longitude coordinate. * Converts a tile X number at a certain zoom level to a longitude
* coordinate.
* *
* @param tileX * @param tileX
* the tile X number that should be converted. * the tile X number that should be converted.
@ -219,7 +229,8 @@ public final class MercatorProjection {
} }
/** /**
* Converts a tile Y number at a certain zoom level to a latitude coordinate. * Converts a tile Y number at a certain zoom level to a latitude
* coordinate.
* *
* @param tileY * @param tileY
* the tile Y number that should be converted. * the tile Y number that should be converted.

View File

@ -0,0 +1,50 @@
/*
* Copyright 2012 Hannes Janetzek
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.utils;
public class FastMath {
/**
* from http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
*
* @param v
* ...
* @return ...
*/
public static int log2(int v) {
int r = 0; // result of log2(v) will go here
if ((v & 0xFFFF0000) != 0) {
v >>= 16;
r |= 16;
}
if ((v & 0xFF00) != 0) {
v >>= 8;
r |= 8;
}
if ((v & 0xF0) != 0) {
v >>= 4;
r |= 4;
}
if ((v & 0xC) != 0) {
v >>= 2;
r |= 2;
}
if ((v & 0x2) != 0) {
r |= 1;
}
return r;
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2012 Hannes Janetzek
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.view;
import android.os.Handler;
import android.os.Message;
public class DelayedTaskHandler extends Handler {
public final int MESSAGE_UPDATE_POSITION = 1;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_UPDATE_POSITION:
break;
}
}
}

View File

@ -83,6 +83,8 @@ public class MapView extends FrameLayout {
private String mRenderTheme; private String mRenderTheme;
private Map<String, String> mMapOptions; private Map<String, String> mMapOptions;
// private final Handler mHandler;
/** /**
* @param context * @param context
* the enclosing MapActivity instance. * the enclosing MapActivity instance.
@ -124,6 +126,8 @@ public class MapView extends FrameLayout {
MapActivity mapActivity = (MapActivity) context; MapActivity mapActivity = (MapActivity) context;
// mHandler = new DelayedTaskHandler();
debugSettings = new DebugSettings(false, false, false, false); debugSettings = new DebugSettings(false, false, false, false);
mMapDatabaseType = mapDatabaseType; mMapDatabaseType = mapDatabaseType;
@ -205,6 +209,29 @@ public class MapView extends FrameLayout {
return mMapViewPosition; return mMapViewPosition;
} }
public void enableRotation(boolean enable) {
enableRotation = enable;
if (enable) {
enableCompass(false);
}
}
public void enableCompass(boolean enable) {
if (enable == this.enableCompass)
return;
this.enableCompass = enable;
if (enable)
enableRotation(false);
if (enable)
mCompass.enable();
else
mCompass.disable();
}
@Override @Override
public boolean onTouchEvent(MotionEvent motionEvent) { public boolean onTouchEvent(MotionEvent motionEvent) {
// mMapZoomControls.onMapViewTouchEvent(motionEvent.getAction() // mMapZoomControls.onMapViewTouchEvent(motionEvent.getAction()
@ -601,30 +628,6 @@ public class MapView extends FrameLayout {
mapWorker.proceed(); mapWorker.proceed();
} }
public void enableRotation(boolean enable) {
enableRotation = enable;
if (enable) {
enableCompass(false);
}
}
public void enableCompass(boolean enable) {
if (enable == this.enableCompass)
return;
this.enableCompass = enable;
if (enable)
enableRotation(false);
if (enable)
mCompass.enable();
else
mCompass.disable();
}
// /** // /**
// * Sets the visibility of the zoom controls. // * Sets the visibility of the zoom controls.
// * // *
@ -647,18 +650,4 @@ public class MapView extends FrameLayout {
// mJobParameters = new JobParameters(mJobParameters.theme, textScale); // mJobParameters = new JobParameters(mJobParameters.theme, textScale);
// clearAndRedrawMapView(); // clearAndRedrawMapView();
// } // }
// public final int
// public Handler messageHandler = new Handler() {
//
// @Override
// public void handleMessage(Message msg) {
// switch (msg.what) {
// // handle update
// // .....
// }
// }
//
// };
} }

View File

@ -17,25 +17,35 @@ package org.oscim.view;
import org.oscim.core.GeoPoint; import org.oscim.core.GeoPoint;
import org.oscim.core.MapPosition; import org.oscim.core.MapPosition;
import org.oscim.core.MercatorProjection; import org.oscim.core.MercatorProjection;
import org.oscim.utils.FastMath;
import android.util.FloatMath;
/** /**
* A MapPosition stores the latitude and longitude coordinate of a MapView together with its zoom level. * A MapPosition stores the latitude and longitude coordinate of a MapView
* together with its zoom level.
*/ */
public class MapViewPosition { public class MapViewPosition {
private static float MAX_SCALE = 2.0f; private static final String TAG = "MapViewPosition";
private static float MIN_SCALE = 1.0f;
public static int MAX_ZOOMLEVEL = 16; public final static int MAX_ZOOMLEVEL = 16;
private final static float MAX_SCALE = 2.0f;
private final static float MIN_SCALE = 1.0f;
private final MapView mMapView;
private double mLatitude; private double mLatitude;
private double mLongitude; private double mLongitude;
private final MapView mMapView;
private byte mZoomLevel; private byte mZoomLevel;
private float mScale; private float mScale;
private float mRotation; private float mRotation;
private float mTilt; private float mTilt;
// 2^mZoomLevel * mScale;
private float mMapScale;
// private final static float MAP_SIZE = 1000000;
// private final static float MAP_SIZE2 = 1000000 >> 1;
MapViewPosition(MapView mapView) { MapViewPosition(MapView mapView) {
mMapView = mapView; mMapView = mapView;
@ -45,8 +55,24 @@ public class MapViewPosition {
mScale = 1; mScale = 1;
mRotation = 0.0f; mRotation = 0.0f;
mTilt = 0; mTilt = 0;
mMapScale = 1;
} }
// private static double latitudeToMapView(double latitude) {
// double sinLatitude = Math.sin(latitude * (Math.PI / 180));
// return (0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 *
// Math.PI))
// * MAP_SIZE;
// }
//
// public static double longitudeToMapView(double longitude) {
// return (longitude + 180) / 360 * MAP_SIZE;
// }
//
// private static double pixelXToLongitude(double pixelX, byte zoomLevel) {
// return 360 * ((pixelX / ((long) Tile.TILE_SIZE << zoomLevel)) - 0.5);
// }
/** /**
* @return the current center point of the MapView. * @return the current center point of the MapView.
*/ */
@ -55,14 +81,16 @@ public class MapViewPosition {
} }
/** /**
* @return an immutable MapPosition or null, if this map position is not valid. * @return an immutable MapPosition or null, if this map position is not
* valid.
* @see #isValid() * @see #isValid()
*/ */
public synchronized MapPosition getMapPosition() { public synchronized MapPosition getMapPosition() {
if (!isValid()) { if (!isValid()) {
return null; return null;
} }
// Log.d("MapViewPosition", "lat: " + mLatitude + " lon: " + mLongitude); // Log.d("MapViewPosition", "lat: " + mLatitude + " lon: " +
// mLongitude);
return new MapPosition(mLatitude, mLongitude, mZoomLevel, mScale, mRotation); return new MapPosition(mLatitude, mLongitude, mZoomLevel, mScale, mRotation);
} }
@ -178,7 +206,8 @@ public class MapViewPosition {
public synchronized void rotateMap(float angle, float cx, float cy) { public synchronized void rotateMap(float angle, float cx, float cy) {
moveMap(cx, cy); moveMap(cx, cy);
// Log.d("MapViewPosition", "rotate:" + angle + " " + (mRotation - angle)); // Log.d("MapViewPosition", "rotate:" + angle + " " + (mRotation -
// angle));
mRotation -= angle; mRotation -= angle;
} }
@ -186,7 +215,7 @@ public class MapViewPosition {
mRotation = f; mRotation = f;
} }
public void setTile(float f) { public void setTilt(float f) {
mTilt = f; mTilt = f;
} }
@ -199,10 +228,12 @@ public class MapViewPosition {
mLatitude = MercatorProjection.limitLatitude(mapPosition.lat); mLatitude = MercatorProjection.limitLatitude(mapPosition.lat);
mLongitude = MercatorProjection.limitLongitude(mapPosition.lon); mLongitude = MercatorProjection.limitLongitude(mapPosition.lon);
mZoomLevel = mMapView.limitZoomLevel(mapPosition.zoomLevel); mZoomLevel = mMapView.limitZoomLevel(mapPosition.zoomLevel);
mMapScale = 1 << mZoomLevel;
} }
synchronized void setZoomLevel(byte zoomLevel) { synchronized void setZoomLevel(byte zoomLevel) {
mZoomLevel = mMapView.limitZoomLevel(zoomLevel); mZoomLevel = mMapView.limitZoomLevel(zoomLevel);
mMapScale = 1 << mZoomLevel;
} }
synchronized void setScale(float scale) { synchronized void setScale(float scale) {
@ -226,79 +257,25 @@ public class MapViewPosition {
moveMap(pivotX * (1.0f - scale), moveMap(pivotX * (1.0f - scale),
pivotY * (1.0f - scale)); pivotY * (1.0f - scale));
float s = mScale * scale; float newScale = mMapScale * scale;
if (s >= MAX_SCALE) { int z = FastMath.log2((int) newScale);
if (s > 8)
if (z <= 0 || (z >= MAX_ZOOMLEVEL && mScale >= 8))
return;
if (z > MAX_ZOOMLEVEL) {
// z16 shows everything, just increase scaling
if (mScale * scale > 8)
return; return;
if (mZoomLevel <= MAX_ZOOMLEVEL) { mScale *= scale;
byte z = (byte) FloatMath.sqrt(s); mMapScale = newScale;
mZoomLevel += z; return;
s *= 1.0f / (1 << z);
}
} else if (s < MIN_SCALE) {
byte z = (byte) FloatMath.sqrt(1 / s);
if (z != 0 && mZoomLevel == 1)
return;
mZoomLevel -= z;
s *= 1 << z;
} }
mScale = s; mZoomLevel = (byte) z;
mScale = newScale / (1 << z);
mMapScale = newScale;
} }
/**
* Zooms in or out by the given amount of zoom levels.
*
* @param zoomLevelDiff
* the difference to the current zoom level.
* @param s
* scale between min/max zoom
* @return true if the zoom level was changed, false otherwise.
*/
// public boolean zoom(byte zoomLevelDiff, float s) {
// float scale = s;
//
// if (zoomLevelDiff > 0) {
// // check if zoom in is possible
// if (mMapViewPosition.getZoomLevel() + zoomLevelDiff > getMaximumPossibleZoomLevel()) {
// return false;
// }
//
// scale *= 1.0f / (1 << zoomLevelDiff);
// } else if (zoomLevelDiff < 0) {
// // check if zoom out is possible
// if (mMapViewPosition.getZoomLevel() + zoomLevelDiff < mMapZoomControls.getZoomLevelMin()) {
// return false;
// }
//
// scale *= 1 << -zoomLevelDiff;
// }
//
// if (scale == 0)
// scale = 1;
// // else
// // scale = Math.round(256.0f * scale) / 256.0f;
//
// mMapViewPosition.setZoomLevel((byte) (mMapViewPosition.getZoomLevel() + zoomLevelDiff));
//
// // mapZoomControls.onZoomLevelChange(mapViewPosition.getZoomLevel());
//
// // zoomAnimator.setParameters(zoomStart, matrixScaleFactor,
// // getWidth() >> 1, getHeight() >> 1);
// // zoomAnimator.startAnimation();
//
// // if (scale > MAX_ZOOM) {
// // scale = MAX_ZOOM;
// // }
//
// if (zoomLevelDiff != 0 || mZoomFactor != scale) {
// mZoomFactor = scale;
// redrawTiles();
// }
//
// return true;
// }
} }

View File

@ -29,7 +29,8 @@ import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller; import android.widget.Scroller;
/** /**
* Implementation for multi-touch capable devices. TODO write a AnimationTimer instead of using CountDownTimer * Implementation for multi-touch capable devices. TODO write a AnimationTimer
* instead of using CountDownTimer
*/ */
public class TouchHandler { public class TouchHandler {
private static final int INVALID_POINTER_ID = -1; private static final int INVALID_POINTER_ID = -1;
@ -440,7 +441,8 @@ public class TouchHandler {
@Override @Override
public void onScaleEnd(ScaleGestureDetector gd) { public void onScaleEnd(ScaleGestureDetector gd) {
// Log.d("ScaleListener", "Sum " + mSumScale + " " + (mTimeEnd - mTimeStart)); // Log.d("ScaleListener", "Sum " + mSumScale + " " + (mTimeEnd -
// mTimeStart));
if (mTimer == null && mTimeEnd - mTimeStart < 150 if (mTimer == null && mTimeEnd - mTimeStart < 150
&& (mSumScale < 0.99 || mSumScale > 1.01)) { && (mSumScale < 0.99 || mSumScale > 1.01)) {
@ -449,7 +451,7 @@ public class TouchHandler {
mZooutOut = mSumScale < 0.99; mZooutOut = mSumScale < 0.99;
mTimer = new CountDownTimer((int) mScaleDuration, 30) { mTimer = new CountDownTimer((int) mScaleDuration, 15) {
@Override @Override
public void onTick(long tick) { public void onTick(long tick) {
scale(tick); scale(tick);
@ -468,7 +470,7 @@ public class TouchHandler {
private float mPrevScale; private float mPrevScale;
private CountDownTimer mTimer; private CountDownTimer mTimer;
boolean mZooutOut; boolean mZooutOut;
private final float mScaleDuration = 350; private final float mScaleDuration = 450;
boolean scale(long tick) { boolean scale(long tick) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2010, 2011, 2012 mapsforge.org * Copyright 2012, Hannes Janetzek
* *
* This program is free software: you can redistribute it and/or modify it under the * This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software * terms of the GNU Lesser General Public License as published by the Free Software

View File

@ -16,19 +16,12 @@ package org.oscim.view.renderer;
import static android.opengl.GLES20.GL_ARRAY_BUFFER; import static android.opengl.GLES20.GL_ARRAY_BUFFER;
import static android.opengl.GLES20.GL_BLEND; import static android.opengl.GLES20.GL_BLEND;
import static android.opengl.GLES20.GL_DEPTH_TEST;
import static android.opengl.GLES20.GL_DYNAMIC_DRAW; import static android.opengl.GLES20.GL_DYNAMIC_DRAW;
import static android.opengl.GLES20.GL_ONE;
import static android.opengl.GLES20.GL_ONE_MINUS_SRC_ALPHA; import static android.opengl.GLES20.GL_ONE_MINUS_SRC_ALPHA;
import static android.opengl.GLES20.GL_POLYGON_OFFSET_FILL;
import static android.opengl.GLES20.GL_STENCIL_BUFFER_BIT; import static android.opengl.GLES20.GL_STENCIL_BUFFER_BIT;
import static android.opengl.GLES20.glBindBuffer;
import static android.opengl.GLES20.glBlendFunc;
import static android.opengl.GLES20.glBufferData;
import static android.opengl.GLES20.glClear;
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glClearStencil;
import static android.opengl.GLES20.glDisable;
import static android.opengl.GLES20.glEnable;
import static android.opengl.GLES20.glGenBuffers;
import static android.opengl.GLES20.glViewport;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
@ -69,7 +62,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
private static ArrayList<VertexBufferObject> mVBOs; private static ArrayList<VertexBufferObject> mVBOs;
private static int mWidth, mHeight; private static int mWidth, mHeight;
private static float mAspect;
private static int rotateBuffers = 2; private static int rotateBuffers = 2;
private static ShortBuffer shortBuffer[]; private static ShortBuffer shortBuffer[];
@ -86,12 +78,12 @@ public class GLRenderer implements GLSurfaceView.Renderer {
private static float[] mProjMatrix = new float[16]; private static float[] mProjMatrix = new float[16];
private static float[] mProjMatrixI = new float[16]; private static float[] mProjMatrixI = new float[16];
// curTiles is set by TileLoader and swapped with // mNextTiles is set by TileLoader and swapped with
// drawTiles in onDrawFrame in GL thread. // mDrawTiles in onDrawFrame in GL thread.
private static TilesData curTiles, drawTiles; private static TilesData mNextTiles, mDrawTiles;
// flag set by updateVisibleList when current visible tiles // flag set by updateVisibleList when current visible tiles
// changed. used in onDrawFrame to flip curTiles/drawTiles // changed. used in onDrawFrame to flip mNextTiles/mDrawTiles
private static boolean mUpdateTiles; private static boolean mUpdateTiles;
private static MapPosition mCurPosition; private static MapPosition mCurPosition;
@ -147,7 +139,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
* current MapPosition * current MapPosition
* @param tiles * @param tiles
* active tiles * active tiles
* @return curTiles (the previously active tiles) * @return mNextTiles (the previously active tiles)
*/ */
static TilesData updateTiles(MapPosition mapPosition, TilesData tiles) { static TilesData updateTiles(MapPosition mapPosition, TilesData tiles) {
GLRenderer.tilelock.lock(); GLRenderer.tilelock.lock();
@ -155,8 +147,8 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mCurPosition = mapPosition; mCurPosition = mapPosition;
// unlock previously active tiles // unlock previously active tiles
for (int i = 0; i < curTiles.cnt; i++) { for (int i = 0; i < mNextTiles.cnt; i++) {
MapTile t = curTiles.tiles[i]; MapTile t = mNextTiles.tiles[i];
boolean found = false; boolean found = false;
for (int j = 0; j < tiles.cnt; j++) { for (int j = 0; j < tiles.cnt; j++) {
@ -168,8 +160,8 @@ public class GLRenderer implements GLSurfaceView.Renderer {
if (found) if (found)
continue; continue;
for (int j = 0; j < drawTiles.cnt; j++) { for (int j = 0; j < mDrawTiles.cnt; j++) {
if (drawTiles.tiles[j] == t) { if (mDrawTiles.tiles[j] == t) {
found = true; found = true;
break; break;
} }
@ -180,22 +172,23 @@ public class GLRenderer implements GLSurfaceView.Renderer {
t.unlock(); t.unlock();
} }
TilesData tmp = curTiles; TilesData tmp = mNextTiles;
curTiles = tiles; mNextTiles = tiles;
// lock tiles (and their proxies) to not be removed from cache // lock tiles (and their proxies) to not be removed from cache
for (int i = 0; i < curTiles.cnt; i++) { for (int i = 0; i < mNextTiles.cnt; i++) {
MapTile t = curTiles.tiles[i]; MapTile t = mNextTiles.tiles[i];
if (!t.isActive) if (!t.isActive)
t.lock(); t.lock();
} }
for (int j = 0; j < drawTiles.cnt; j++) { for (int j = 0; j < mDrawTiles.cnt; j++) {
MapTile t = drawTiles.tiles[j]; MapTile t = mDrawTiles.tiles[j];
if (!t.isActive) if (!t.isActive)
t.lock(); t.lock();
} }
// GLThread flips mNextTiles with mDrawTiles
mUpdateTiles = true; mUpdateTiles = true;
GLRenderer.tilelock.unlock(); GLRenderer.tilelock.unlock();
@ -236,46 +229,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mUpdateColor = true; mUpdateColor = true;
} }
private static boolean isVisible(MapPosition mapPosition, MapTile tile, float div) {
double dx, dy, scale;
if (div == 0) {
dx = tile.pixelX - mapPosition.x;
dy = tile.pixelY - mapPosition.y;
scale = mapPosition.scale;
} else {
dx = tile.pixelX - mapPosition.x * div;
dy = tile.pixelY - mapPosition.y * div;
scale = mapPosition.scale / div;
}
int size = Tile.TILE_SIZE;
int sx = (int) (dx * scale);
int sy = (int) (dy * scale);
// FIXME little hack, need to do scanline check or sth
// this kindof works for typical screen aspect
if (mRotate) {
int ssize = mWidth > mHeight ? mWidth : mHeight;
if (sy > ssize / 2 || sx > ssize / 2
|| sx + size * scale < -ssize / 2
|| sy + size * scale < -ssize / 2) {
tile.isVisible = false;
return false;
}
} else {
if (sy > mHeight / 2 || sx > mWidth / 2
|| sx + size * scale < -mWidth / 2
|| sy + size * scale < -mHeight / 2) {
tile.isVisible = false;
return false;
}
}
tile.isVisible = true;
return true;
}
private int uploadCnt = 0; private int uploadCnt = 0;
private boolean uploadTileData(MapTile tile) { private boolean uploadTileData(MapTile tile) {
@ -308,7 +261,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
} }
// Log.d(TAG, "uploadTileData, " + tile); // Log.d(TAG, "uploadTileData, " + tile);
glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id); GLES20.glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id);
sbuf = shortBuffer[uploadCnt]; sbuf = shortBuffer[uploadCnt];
@ -317,8 +270,8 @@ public class GLRenderer implements GLSurfaceView.Renderer {
// FIXME probably not a good idea to do this in gl thread... // FIXME probably not a good idea to do this in gl thread...
if (sbuf == null || sbuf.capacity() < newSize) { if (sbuf == null || sbuf.capacity() < newSize) {
ByteBuffer bbuf = ByteBuffer.allocateDirect(newSize * SHORT_BYTES).order( ByteBuffer bbuf = ByteBuffer.allocateDirect(newSize * SHORT_BYTES)
ByteOrder.nativeOrder()); .order(ByteOrder.nativeOrder());
sbuf = bbuf.asShortBuffer(); sbuf = bbuf.asShortBuffer();
shortBuffer[uploadCnt] = sbuf; shortBuffer[uploadCnt] = sbuf;
sbuf.put(mFillCoords, 0, 8); sbuf.put(mFillCoords, 0, 8);
@ -346,7 +299,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
sbuf.flip(); sbuf.flip();
if (newSize != sbuf.remaining()) { if (newSize != sbuf.remaining()) {
Log.d(TAG, "tiles wrong: " + tile + " " Log.d(TAG, "tiles wrong: " + tile + " "
+ newSize + " " + newSize + " "
+ sbuf.position() + " " + sbuf.position() + " "
@ -361,13 +313,16 @@ public class GLRenderer implements GLSurfaceView.Renderer {
} }
newSize *= SHORT_BYTES; newSize *= SHORT_BYTES;
// reuse memory allocated for vbo when possible and allocated
// memory is less then four times the new data
if (tile.vbo.size > newSize && tile.vbo.size < newSize * 4 if (tile.vbo.size > newSize && tile.vbo.size < newSize * 4
&& mBufferMemoryUsage < LIMIT_BUFFERS) { && mBufferMemoryUsage < LIMIT_BUFFERS) {
GLES20.glBufferSubData(GL_ARRAY_BUFFER, 0, newSize, sbuf); GLES20.glBufferSubData(GL_ARRAY_BUFFER, 0, newSize, sbuf);
// Log.d(TAG, "reuse buffer " + tile.vbo.size + " " + newSize);
} else { } else {
mBufferMemoryUsage -= tile.vbo.size; mBufferMemoryUsage -= tile.vbo.size;
tile.vbo.size = newSize; tile.vbo.size = newSize;
glBufferData(GL_ARRAY_BUFFER, tile.vbo.size, sbuf, GL_DYNAMIC_DRAW); GLES20.glBufferData(GL_ARRAY_BUFFER, tile.vbo.size, sbuf, GL_DYNAMIC_DRAW);
mBufferMemoryUsage += tile.vbo.size; mBufferMemoryUsage += tile.vbo.size;
} }
@ -380,47 +335,96 @@ public class GLRenderer implements GLSurfaceView.Renderer {
} }
private static void checkBufferUsage() { private static void checkBufferUsage() {
// try to clear some unused vbo when exceding limit if (mBufferMemoryUsage < LIMIT_BUFFERS) {
if (mBufferMemoryUsage > LIMIT_BUFFERS) { if (CACHE_TILES < CACHE_TILES_MAX)
Log.d(TAG, "buffer object usage: " + mBufferMemoryUsage / MB + "MB"); CACHE_TILES += 50;
return;
glBindBuffer(GL_ARRAY_BUFFER, 0);
int buf[] = new int[1];
synchronized (mVBOs) {
for (VertexBufferObject vbo : mVBOs) {
if (vbo.size == 0)
continue;
mBufferMemoryUsage -= vbo.size;
// this should free allocated memory but it does not.
// on HTC it causes oom exception?!
// glBindBuffer(GL_ARRAY_BUFFER, vbo.id);
// glBufferData(GL_ARRAY_BUFFER, 0, null,
// GLES20.GL_STATIC_DRAW);
// recreate vbo instead
buf[0] = vbo.id;
GLES20.glDeleteBuffers(1, buf, 0);
GLES20.glGenBuffers(1, buf, 0);
vbo.id = buf[0];
vbo.size = 0;
}
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
Log.d(TAG, " > " + mBufferMemoryUsage / MB + "MB");
if (mBufferMemoryUsage > LIMIT_BUFFERS && CACHE_TILES > 100)
CACHE_TILES -= 50;
} else if (CACHE_TILES < CACHE_TILES_MAX) {
CACHE_TILES += 50;
} }
// try to clear some unused vbo when exceding limit
Log.d(TAG, "buffer object usage: " + mBufferMemoryUsage / MB + "MB");
int vboIds[] = new int[10];
VertexBufferObject[] tmp = new VertexBufferObject[10];
int removed = 0;
synchronized (mVBOs) {
for (VertexBufferObject vbo : mVBOs) {
if (vbo.size == 0)
continue;
mBufferMemoryUsage -= vbo.size;
vbo.size = 0;
// this should free allocated memory but it does not.
// on HTC it causes OOM exception?!
// glBindBuffer(GL_ARRAY_BUFFER, vbo.id);
// glBufferData(GL_ARRAY_BUFFER, 0, null,
// GLES20.GL_STATIC_DRAW);
// recreate vbo instead
vboIds[removed] = vbo.id;
tmp[removed++] = vbo;
if (removed == 10)
break;
}
}
if (removed > 0) {
GLES20.glDeleteBuffers(removed, vboIds, 0);
GLES20.glGenBuffers(removed, vboIds, 0);
for (int i = 0; i < removed; i++)
tmp[i].id = vboIds[i];
Log.d(TAG, "now: " + mBufferMemoryUsage / MB + "MB");
}
if (mBufferMemoryUsage > LIMIT_BUFFERS && CACHE_TILES > 100)
CACHE_TILES -= 50;
}
private static boolean isVisible(MapPosition mapPosition, MapTile tile) {
float dx, dy, scale, div = 1;
int diff = mapPosition.zoomLevel - tile.zoomLevel;
if (diff < 0)
div = (1 << -diff);
else if (diff > 0)
div = (1.0f / (1 << diff));
scale = mapPosition.scale / div;
dx = (float) (tile.pixelX - mapPosition.x * div);
dy = (float) (tile.pixelY - mapPosition.y * div);
int size = Tile.TILE_SIZE;
int sx = (int) (dx * scale);
int sy = (int) (dy * scale);
// FIXME little hack, need to do scanline check or sth
// this kindof works for typical screen aspect
if (mRotate) {
int ssize = mWidth > mHeight ? mWidth : mHeight;
if (sy > ssize / 2 || sx > ssize / 2
|| sx + size * scale < -ssize / 2
|| sy + size * scale < -ssize / 2) {
tile.isVisible = false;
return false;
}
} else {
if (sy > mHeight / 2 || sx > mWidth / 2
|| sx + size * scale < -mWidth / 2
|| sy + size * scale < -mHeight / 2) {
tile.isVisible = false;
return false;
}
}
tile.isVisible = true;
return true;
} }
private static boolean mRotate = false; private static boolean mRotate = false;
@ -429,17 +433,16 @@ public class GLRenderer implements GLSurfaceView.Renderer {
float div, boolean project) { float div, boolean project) {
float x, y, scale; float x, y, scale;
// if (mRotate) {
scale = mapPosition.scale / (div * COORD_MULTIPLIER); scale = mapPosition.scale / (div * COORD_MULTIPLIER);
x = (float) (tile.pixelX - mapPosition.x * div); x = (float) (tile.pixelX - mapPosition.x * div);
y = (float) (tile.pixelY - mapPosition.y * div); y = (float) (tile.pixelY - mapPosition.y * div);
Matrix.setIdentityM(matrix, 0); Matrix.setIdentityM(matrix, 0);
// scale to tile coordinates // scale to tile to world coordinates
Matrix.scaleM(matrix, 0, scale, scale, 1); Matrix.scaleM(matrix, 0, scale, scale, 1);
// translate relative to center // translate relative to map center
Matrix.translateM(matrix, 0, Matrix.translateM(matrix, 0,
x * COORD_MULTIPLIER, x * COORD_MULTIPLIER,
-(y + Tile.TILE_SIZE) * COORD_MULTIPLIER, -(y + Tile.TILE_SIZE) * COORD_MULTIPLIER,
@ -450,44 +453,31 @@ public class GLRenderer implements GLSurfaceView.Renderer {
if (project) if (project)
Matrix.multiplyMM(matrix, 0, mProjMatrix, 0, matrix, 0); Matrix.multiplyMM(matrix, 0, mProjMatrix, 0, matrix, 0);
// } else {
// scale = (float) (2.0 * mapPosition.scale / (mHeight * div));
// x = (float) (tile.pixelX - mapPosition.x * div);
// y = (float) (tile.pixelY - mapPosition.y * div);
//
// matrix[12] = x * scale * mAspect;
// matrix[13] = -(y + Tile.TILE_SIZE) * scale;
// // increase the 'distance' with each tile drawn.
// matrix[14] = -0.99f + offset * 0.01f;
// matrix[0] = scale * mAspect / COORD_MULTIPLIER;
// matrix[5] = scale / COORD_MULTIPLIER;
// }
} }
private float[] mv = new float[4]; // private float[] mv = new float[4];
private float[] mu = { 1, 1, -1, 1 }; // private float[] mu = { 1, 1, -1, 1 };
//
private float[] mUnprojMatrix = new float[16]; // private float[] mUnprojMatrix = new float[16];
//
private void unproject(MapPosition pos, float x, float y) { // private void unproject(MapPosition pos, float x, float y) {
mu[0] = x; // mu[0] = x;
mu[1] = y; // mu[1] = y;
mu[2] = -1; // mu[2] = -1;
//
// add tilt // // add tilt
Matrix.multiplyMV(mv, 0, mTmpMatrix, 0, mu, 0); // Matrix.multiplyMV(mv, 0, mTmpMatrix, 0, mu, 0);
// Log.d(TAG, ">> " + mv[0] + " " + mv[1] + " " + mv[2]); // // Log.d(TAG, ">> " + mv[0] + " " + mv[1] + " " + mv[2]);
//
Matrix.multiplyMV(mv, 0, mUnprojMatrix, 0, mv, 0); // Matrix.multiplyMV(mv, 0, mUnprojMatrix, 0, mv, 0);
float size = Tile.TILE_SIZE * pos.scale; // float size = Tile.TILE_SIZE * pos.scale;
if (mv[3] != 0) { // if (mv[3] != 0) {
float w = 1 / mv[3]; // float w = 1 / mv[3];
float xx = Math.round(((mv[0] * w) / size) * 100) / 100f; // float xx = Math.round(((mv[0] * w) / size) * 100) / 100f;
float yy = Math.round(((mv[1] * w) / size) * 100) / 100f; // float yy = Math.round(((mv[1] * w) / size) * 100) / 100f;
Log.d(TAG, " " + xx + " " + yy); // Log.d(TAG, " " + xx + " " + yy);
} // }
} // }
@Override @Override
public void onDrawFrame(GL10 glUnused) { public void onDrawFrame(GL10 glUnused) {
@ -498,30 +488,27 @@ public class GLRenderer implements GLSurfaceView.Renderer {
if (MapView.debugFrameTime) if (MapView.debugFrameTime)
start = SystemClock.uptimeMillis(); start = SystemClock.uptimeMillis();
// Note: it seems faster to also clear the stencil buffer even
// when not needed. probaly otherwise it is masked out from the
// depth buffer as they share the same memory region afaik
GLES20.glDepthMask(true); GLES20.glDepthMask(true);
GLES20.glStencilMask(0xFF);
// Note: having the impression it is faster to also clear the GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT
// stencil buffer even when not needed. probaly otherwise it
// is masked out from the depth buffer as they share the same
// memory region afaik
glClear(GLES20.GL_COLOR_BUFFER_BIT
| GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT
// | GLES20.GL_STENCIL_BUFFER_BIT | GLES20.GL_STENCIL_BUFFER_BIT);
);
// get position and current tiles to draw // get position and current tiles to draw
GLRenderer.tilelock.lock(); GLRenderer.tilelock.lock();
mapPosition = mCurPosition; mapPosition = mCurPosition;
if (mUpdateTiles) { if (mUpdateTiles) {
TilesData tmp = drawTiles; TilesData tmp = mDrawTiles;
drawTiles = curTiles; mDrawTiles = mNextTiles;
curTiles = tmp; mNextTiles = tmp;
mUpdateTiles = false; mUpdateTiles = false;
} }
GLRenderer.tilelock.unlock(); GLRenderer.tilelock.unlock();
if (drawTiles == null) if (mDrawTiles == null)
return; return;
// if (mRotate != (mMapView.enableRotation || mMapView.enableCompass)) { // if (mRotate != (mMapView.enableRotation || mMapView.enableCompass)) {
@ -572,12 +559,13 @@ public class GLRenderer implements GLSurfaceView.Renderer {
} }
if (mUpdateColor && mClearColor != null) { if (mUpdateColor && mClearColor != null) {
glClearColor(mClearColor[0], mClearColor[1], mClearColor[2], mClearColor[3]); GLES20.glClearColor(mClearColor[0], mClearColor[1], mClearColor[2],
mClearColor[3]);
mUpdateColor = false; mUpdateColor = false;
} }
int tileCnt = drawTiles.cnt; int tileCnt = mDrawTiles.cnt;
MapTile[] tiles = drawTiles.tiles; MapTile[] tiles = mDrawTiles.tiles;
uploadCnt = 0; uploadCnt = 0;
int updateTextures = 0; int updateTextures = 0;
@ -586,7 +574,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
for (int i = 0; i < tileCnt; i++) { for (int i = 0; i < tileCnt; i++) {
MapTile tile = tiles[i]; MapTile tile = tiles[i];
if (!isVisible(mapPosition, tile, 1)) if (!isVisible(mapPosition, tile))
continue; continue;
if (tile.texture == null && TextRenderer.drawToTexture(tile)) if (tile.texture == null && TextRenderer.drawToTexture(tile))
@ -621,32 +609,29 @@ public class GLRenderer implements GLSurfaceView.Renderer {
if (updateTextures > 0) if (updateTextures > 0)
TextRenderer.compileTextures(); TextRenderer.compileTextures();
GLES20.glEnable(GLES20.GL_DEPTH_TEST); GLES20.glEnable(GL_DEPTH_TEST);
GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL); GLES20.glEnable(GL_POLYGON_OFFSET_FILL);
for (int i = 0; i < tileCnt; i++) { for (int i = 0; i < tileCnt; i++) {
if (tiles[i].isVisible && tiles[i].isReady) { if (tiles[i].isVisible && tiles[i].isReady)
drawTile(mapPosition, tiles[i], 1); drawTile(mapPosition, tiles[i]);
}
} }
// proxies are clipped to the region where nothing was drawn to depth // proxies are clipped to the region where nothing was drawn to depth
// buffer // buffer. TODO draw all parent before grandparent
// TODO draw all parent before grandparent
for (int i = 0; i < tileCnt; i++) { for (int i = 0; i < tileCnt; i++) {
if (tiles[i].isVisible && !tiles[i].isReady) { if (tiles[i].isVisible && !tiles[i].isReady)
drawProxyTile(mapPosition, tiles[i]); drawProxyTile(mapPosition, tiles[i]);
}
} }
// GlUtils.checkGlError("end draw"); // GlUtils.checkGlError("end draw");
GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL); GLES20.glDisable(GL_POLYGON_OFFSET_FILL);
GLES20.glDisable(GLES20.GL_DEPTH_TEST); GLES20.glDisable(GL_DEPTH_TEST);
mDrawCount = 0; mDrawCount = 0;
mDrawSerial++; mDrawSerial++;
glEnable(GL_BLEND); GLES20.glEnable(GL_BLEND);
int z = mapPosition.zoomLevel; int z = mapPosition.zoomLevel;
float s = mapPosition.scale; float s = mapPosition.scale;
@ -683,11 +668,20 @@ public class GLRenderer implements GLSurfaceView.Renderer {
// used to not draw a tile twice per frame... // used to not draw a tile twice per frame...
private static byte mDrawSerial = 0; private static byte mDrawSerial = 0;
private static void drawTile(MapPosition mapPosition, MapTile tile, float div) { private static void drawTile(MapPosition mapPosition, MapTile tile) {
// draw parents only once // draw parents only once
if (tile.lastDraw == mDrawSerial) if (tile.lastDraw == mDrawSerial)
return; return;
float div = 1;
int diff = mapPosition.zoomLevel - tile.zoomLevel;
if (diff < 0)
div = (1 << -diff);
else if (diff > 0)
div = (1.0f / (1 << diff));
tile.lastDraw = mDrawSerial; tile.lastDraw = mDrawSerial;
int z = mapPosition.zoomLevel; int z = mapPosition.zoomLevel;
@ -698,7 +692,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
GLES20.glPolygonOffset(0, mDrawCount++); GLES20.glPolygonOffset(0, mDrawCount++);
glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id); GLES20.glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id);
LineLayer ll = tile.lineLayers; LineLayer ll = tile.lineLayers;
PolygonLayer pl = tile.polygonLayers; PolygonLayer pl = tile.polygonLayers;
@ -716,10 +710,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
pnext = pl.layer; pnext = pl.layer;
if (pl != null && pnext < lnext) { if (pl != null && pnext < lnext) {
glDisable(GL_BLEND); GLES20.glDisable(GL_BLEND);
pl = PolygonRenderer.drawPolygons(pl, lnext, mvp, z, s, pl = PolygonRenderer.drawPolygons(pl, lnext, mvp, z, s, !clipped);
!clipped);
clipped = true; clipped = true;
@ -730,7 +723,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
clipped = true; clipped = true;
} }
glEnable(GL_BLEND); GLES20.glEnable(GL_BLEND);
ll = LineRenderer.drawLines(tile, ll, pnext, mvp, div, z, s); ll = LineRenderer.drawLines(tile, ll, pnext, mvp, div, z, s);
} }
} }
@ -747,13 +740,13 @@ public class GLRenderer implements GLSurfaceView.Renderer {
if (c == null) if (c == null)
continue; continue;
if (!isVisible(mapPosition, c, 2)) { if (!isVisible(mapPosition, c)) {
drawn++; drawn++;
continue; continue;
} }
if (c.isReady) { if (c.isReady) {
drawTile(mapPosition, c, 2); drawTile(mapPosition, c);
drawn++; drawn++;
} }
} }
@ -762,36 +755,38 @@ public class GLRenderer implements GLSurfaceView.Renderer {
// TODO could use tile.proxies here // TODO could use tile.proxies here
private static void drawProxyTile(MapPosition mapPosition, MapTile tile) { private static void drawProxyTile(MapPosition mapPosition, MapTile tile) {
boolean drawn = false;
if (mapPosition.scale > 1.5f) { if (mapPosition.scale > 1.5f) {
// prefer drawing children // prefer drawing children
if (!drawProxyChild(mapPosition, tile)) { if (!drawProxyChild(mapPosition, tile)) {
MapTile t = tile.rel.parent.tile; if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
if (t != null) { MapTile t = tile.rel.parent.tile;
if (t.isReady) { if (t.isReady) {
drawTile(mapPosition, t, 0.5f); drawTile(mapPosition, t);
} else { drawn = true;
MapTile p = t.rel.parent.tile;
if (p != null && p.isReady)
drawTile(mapPosition, p, 0.25f);
} }
} }
if (!drawn && (tile.proxies & MapTile.PROXY_GRAMPA) != 0) {
MapTile t = tile.rel.parent.parent.tile;
if (t.isReady)
drawTile(mapPosition, t);
}
} }
} else { } else {
// prefer drawing parent // prefer drawing parent
MapTile t = tile.rel.parent.tile; MapTile t = tile.rel.parent.tile;
if (t != null && t.isReady) { if (t != null && t.isReady) {
drawTile(mapPosition, t, 0.5f); drawTile(mapPosition, t);
} else if (!drawProxyChild(mapPosition, tile)) { } else if (!drawProxyChild(mapPosition, tile)) {
// need to check rel.parent here, t could alread be root if ((tile.proxies & MapTile.PROXY_GRAMPA) != 0) {
if (t != null) { t = tile.rel.parent.parent.tile;
t = t.rel.parent.tile; if (t.isReady)
drawTile(mapPosition, t);
if (t != null && t.isReady)
drawTile(mapPosition, t, 0.25f);
} }
} }
} }
@ -811,8 +806,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mWidth = width; mWidth = width;
mHeight = height; mHeight = height;
mAspect = (float) height / width;
// Matrix.orthoM(mProjMatrix, 0, -0.5f / mAspect, 0.5f / mAspect, -0.5f, // Matrix.orthoM(mProjMatrix, 0, -0.5f / mAspect, 0.5f / mAspect, -0.5f,
// 0.5f, -1, 1); // 0.5f, -1, 1);
@ -828,7 +821,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mProjMatrix[10] = 0; mProjMatrix[10] = 0;
mProjMatrix[14] = 0; mProjMatrix[14] = 0;
glViewport(0, 0, width, height); GLES20.glViewport(0, 0, width, height);
if (!changed && !mNewSurface) { if (!changed && !mNewSurface) {
mMapView.redrawMap(); mMapView.redrawMap();
@ -845,7 +838,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
// Set up vertex buffer objects // Set up vertex buffer objects
int numVBO = (CACHE_TILES + (numTiles * 2)); int numVBO = (CACHE_TILES + (numTiles * 2));
int[] mVboIds = new int[numVBO]; int[] mVboIds = new int[numVBO];
glGenBuffers(numVBO, mVboIds, 0); GLES20.glGenBuffers(numVBO, mVboIds, 0);
GlUtils.checkGlError("glGenBuffers"); GlUtils.checkGlError("glGenBuffers");
mVBOs = new ArrayList<VertexBufferObject>(numVBO); mVBOs = new ArrayList<VertexBufferObject>(numVBO);
@ -857,22 +850,22 @@ public class GLRenderer implements GLSurfaceView.Renderer {
TextRenderer.setup(numTiles); TextRenderer.setup(numTiles);
if (mClearColor != null) { if (mClearColor != null) {
glClearColor(mClearColor[0], mClearColor[1], GLES20.glClearColor(mClearColor[0], mClearColor[1],
mClearColor[2], mClearColor[3]); mClearColor[2], mClearColor[3]);
} else { } else {
glClearColor(0.98f, 0.98f, 0.97f, 1.0f); GLES20.glClearColor(0.98f, 0.98f, 0.97f, 1.0f);
} }
GlUtils.checkGlError("onSurfaceChanged"); GlUtils.checkGlError("onSurfaceChanged");
glClear(GL_STENCIL_BUFFER_BIT); GLES20.glClear(GL_STENCIL_BUFFER_BIT);
mMapView.redrawMap(); mMapView.redrawMap();
} }
void clearTiles(int numTiles) { void clearTiles(int numTiles) {
drawTiles = new TilesData(numTiles); mDrawTiles = new TilesData(numTiles);
curTiles = new TilesData(numTiles); mNextTiles = new TilesData(numTiles);
} }
@Override @Override
@ -904,9 +897,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
// glEnable(GL_SCISSOR_TEST); // glEnable(GL_SCISSOR_TEST);
// glScissor(0, 0, mWidth, mHeight); // glScissor(0, 0, mWidth, mHeight);
glClearStencil(0); GLES20.glClearStencil(0);
glDisable(GLES20.GL_CULL_FACE); GLES20.glDisable(GLES20.GL_CULL_FACE);
glBlendFunc(GLES20.GL_ONE, GL_ONE_MINUS_SRC_ALPHA); GLES20.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
} }

View File

@ -25,7 +25,8 @@ class LineLayer {
private static final float COORD_SCALE = GLRenderer.COORD_MULTIPLIER; private static final float COORD_SCALE = GLRenderer.COORD_MULTIPLIER;
// scale factor mapping extrusion vector to short values // scale factor mapping extrusion vector to short values
private static final float DIR_SCALE = 2048; private static final float DIR_SCALE = 2048;
// mask for packing last two bits of extrusion vector with texture coordinates // mask for packing last two bits of extrusion vector with texture
// coordinates
private static final int DIR_MASK = 0xFFFFFFFC; private static final int DIR_MASK = 0xFFFFFFFC;
// next layer // next layer
@ -64,7 +65,8 @@ class LineLayer {
} }
/* /*
* line extrusion is based on code from GLMap (https://github.com/olofsj/GLMap/) by olofsj * line extrusion is based on code from GLMap
* (https://github.com/olofsj/GLMap/) by olofsj
*/ */
void addLine(float[] points, short[] index, boolean closed) { void addLine(float[] points, short[] index, boolean closed) {

View File

@ -62,11 +62,10 @@ class LineRenderer {
static LineLayer drawLines(MapTile tile, LineLayer layer, int next, float[] matrix, static LineLayer drawLines(MapTile tile, LineLayer layer, int next, float[] matrix,
float div, double zoom, float scale) { float div, double zoom, float scale) {
float z = 1 / div;
if (layer == null) if (layer == null)
return null; return null;
// TODO should use fast line program when view is not tilted
GLES20.glUseProgram(lineProgram); GLES20.glUseProgram(lineProgram);
GLES20.glEnableVertexAttribArray(hLineVertexPosition); GLES20.glEnableVertexAttribArray(hLineVertexPosition);
@ -81,7 +80,9 @@ class LineRenderer {
GLES20.glUniformMatrix4fv(hLineMatrix, 1, false, matrix, 0); GLES20.glUniformMatrix4fv(hLineMatrix, 1, false, matrix, 0);
// scale factor to map one pixel on tile to one pixel on screen: // scale factor to map one pixel on tile to one pixel on screen:
float pixel = 2.0f / (scale * z); // only works with orthographic projection
float s = scale / div;
float pixel = 2.0f / s;
if (mFast) if (mFast)
GLES20.glUniform1f(hLineScale, pixel); GLES20.glUniform1f(hLineScale, pixel);
@ -89,7 +90,7 @@ class LineRenderer {
GLES20.glUniform1f(hLineScale, 0); GLES20.glUniform1f(hLineScale, 0);
// line scale factor (for non fixed lines) // line scale factor (for non fixed lines)
float s = FloatMath.sqrt(scale * z); float lineScale = FloatMath.sqrt(s);
boolean blur = false; boolean blur = false;
LineLayer l = layer; LineLayer l = layer;
@ -117,33 +118,31 @@ class LineRenderer {
for (LineLayer o = l.outlines; o != null; o = o.outlines) { for (LineLayer o = l.outlines; o != null; o = o.outlines) {
if (line.blur != 0) { if (line.blur != 0) {
GLES20.glUniform1f(hLineScale, (l.width + o.width) / (scale * z) GLES20.glUniform1f(hLineScale, (l.width + o.width) / s
- (line.blur / (scale * z))); - (line.blur / s));
blur = true; blur = true;
} }
if (zoom > TileGenerator.STROKE_MAX_ZOOM_LEVEL) if (zoom > TileGenerator.STROKE_MAX_ZOOM_LEVEL)
GLES20.glUniform1f(hLineWidth, GLES20.glUniform1f(hLineWidth, (l.width + o.width) / s);
(l.width + o.width) / (scale * z));
else else
GLES20.glUniform1f(hLineWidth, l.width / (scale * z) GLES20.glUniform1f(hLineWidth, l.width / s + o.width / lineScale);
+ o.width / s);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, o.offset, o.verticesCnt); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, o.offset, o.verticesCnt);
} }
} }
else { else {
if (line.blur != 0) { if (line.blur != 0) {
GLES20.glUniform1f(hLineScale, (l.width / s) * line.blur); GLES20.glUniform1f(hLineScale, (l.width / lineScale) * line.blur);
blur = true; blur = true;
} }
if (line.fixed || zoom > TileGenerator.STROKE_MAX_ZOOM_LEVEL) { if (line.fixed || zoom > TileGenerator.STROKE_MAX_ZOOM_LEVEL) {
// invert scaling of extrusion vectors so that line width // invert scaling of extrusion vectors so that line width
// stays the same // stays the same.
GLES20.glUniform1f(hLineWidth, (l.width / (scale * z))); GLES20.glUniform1f(hLineWidth, l.width / s);
} else { } else {
GLES20.glUniform1f(hLineWidth, (l.width / s)); GLES20.glUniform1f(hLineWidth, l.width / lineScale);
} }
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, l.offset, l.verticesCnt); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, l.offset, l.verticesCnt);

View File

@ -91,13 +91,10 @@ public class MapRenderer extends GLSurfaceView {
mInitial = true; mInitial = true;
} }
/**
* called by MapView when position or map settings changes
*/
/** /**
* Update list of visible tiles and passes them to MapRenderer, when not * Update list of visible tiles and passes them to MapRenderer, when not
* available tiles are created and added to JobQueue (mapView.addJobs) for * available tiles are created and added to JobQueue (mapView.addJobs) for
* loading by MapGenerator class * loading by TileGenerator class
* *
* @param clear * @param clear
* ... * ...
@ -108,7 +105,7 @@ public class MapRenderer extends GLSurfaceView {
boolean changedPos = false; boolean changedPos = false;
boolean changedZoom = false; boolean changedZoom = false;
if (mMapView == null || mMapView.getMapPosition() == null) if (mMapView == null)
return; return;
MapPosition mapPosition = mMapView.getMapPosition().getMapPosition(); MapPosition mapPosition = mMapView.getMapPosition().getMapPosition();
@ -121,14 +118,17 @@ public class MapRenderer extends GLSurfaceView {
if (clear) { if (clear) {
// remove all tiles references // remove all tiles references
Log.d(TAG, "CLEAR"); Log.d(TAG, "CLEAR");
synchronized (GLRenderer.lock) {
for (MapTile t : mTiles)
clearTile(t);
mTiles.clear(); GLRenderer.tilelock.lock();
mTilesLoaded.clear();
QuadTree.init(); for (MapTile t : mTiles)
} clearTile(t);
mTiles.clear();
mTilesLoaded.clear();
QuadTree.init();
GLRenderer.tilelock.unlock();
mInitial = true; mInitial = true;
} }
@ -180,21 +180,29 @@ public class MapRenderer extends GLSurfaceView {
mTileY = tileY; mTileY = tileY;
mPrevZoom = zoomLevel; mPrevZoom = zoomLevel;
if (changedZoom) { GLRenderer.updatePosition(mapPosition);
// need to update visible list first when zoom level changes
// as scaling is relative to the tiles of current zoom-level
updateVisibleList(mapPosition, 0);
} else {
// pass new position to glThread
GLRenderer.updatePosition(mapPosition);
}
if (!MapView.debugFrameTime) if (!MapView.debugFrameTime)
requestRender(); requestRender();
if (changedPos) if (changedZoom || changedPos) {
// need to update visible list first when zoom level changes
// as scaling is relative to the tiles of current zoom-level
updateVisibleList(mapPosition, zdir); updateVisibleList(mapPosition, zdir);
if (!MapView.debugFrameTime)
requestRender();
}
// else {
// // pass new position to glThread
// GLRenderer.updatePosition(mapPosition);
// }
// if (!MapView.debugFrameTime)
// requestRender();
// if (changedPos)
// updateVisibleList(mapPosition, zdir);
if (changedPos || changedZoom) { if (changedPos || changedZoom) {
int remove = mTiles.size() - GLRenderer.CACHE_TILES; int remove = mTiles.size() - GLRenderer.CACHE_TILES;
if (remove > 50) if (remove > 50)
@ -202,7 +210,6 @@ public class MapRenderer extends GLSurfaceView {
} }
limitLoadQueue(); limitLoadQueue();
} }
/** /**

View File

@ -19,7 +19,8 @@ import org.oscim.view.generator.JobTile;
class MapTile extends JobTile { class MapTile extends JobTile {
/** /**
* VBO layout: - 16 bytes fill coordinates, n bytes polygon vertices, m bytes lines vertices * VBO layout: - 16 bytes fill coordinates, n bytes polygon vertices, m
* bytes lines vertices
*/ */
VertexBufferObject vbo; VertexBufferObject vbo;
@ -65,8 +66,8 @@ class MapTile extends JobTile {
byte lastDraw = 0; byte lastDraw = 0;
// keep track which tiles are locked as proxy for this tile // keep track which tiles are locked as proxy for this tile
// 16: parent final static int PROXY_PARENT = 16;
// 32: grandparent final static int PROXY_GRAMPA = 32;
// 1-8: children // 1-8: children
byte proxies; byte proxies;
@ -99,12 +100,12 @@ class MapTile extends JobTile {
MapTile p = rel.parent.tile; MapTile p = rel.parent.tile;
if (p != null && (p.isReady || p.newData || p.isLoading)) { if (p != null && (p.isReady || p.newData || p.isLoading)) {
proxies |= (1 << 4); proxies |= PROXY_PARENT;
p.refs++; p.refs++;
} else { } else {
p = rel.parent.parent.tile; p = rel.parent.parent.tile;
if (p != null && (p.isReady || p.newData || p.isLoading)) { if (p != null && (p.isReady || p.newData || p.isLoading)) {
proxies |= (1 << 5); proxies |= PROXY_GRAMPA;
p.refs++; p.refs++;
} }
} }

View File

@ -42,6 +42,8 @@ import org.oscim.utils.GlUtils;
import android.opengl.GLES20; import android.opengl.GLES20;
class PolygonRenderer { class PolygonRenderer {
private static final String TAG = "PolygonRenderer";
private static final int NUM_VERTEX_SHORTS = 2; private static final int NUM_VERTEX_SHORTS = 2;
private static final int POLYGON_VERTICES_DATA_POS_OFFSET = 0; private static final int POLYGON_VERTICES_DATA_POS_OFFSET = 0;
private static int STENCIL_BITS = 8; private static int STENCIL_BITS = 8;
@ -71,7 +73,7 @@ class PolygonRenderer {
return true; return true;
} }
private static void fillPolygons(int count, double zoom, float scale) { private static void fillPolygons(double zoom, float scale) {
boolean blend = false; boolean blend = false;
/* draw to framebuffer */ /* draw to framebuffer */
@ -83,7 +85,7 @@ class PolygonRenderer {
/* only draw where nothing was drawn yet */ /* only draw where nothing was drawn yet */
glEnable(GLES20.GL_DEPTH_TEST); glEnable(GLES20.GL_DEPTH_TEST);
for (int c = 0; c < count; c++) { for (int c = mStart; c < mCount; c++) {
PolygonLayer l = mFillPolys[c]; PolygonLayer l = mFillPolys[c];
float f = 1.0f; float f = 1.0f;
@ -149,8 +151,13 @@ class PolygonRenderer {
glDisable(GL_BLEND); glDisable(GL_BLEND);
} }
static PolygonLayer drawPolygons(PolygonLayer layer, int next, // layers to fill
float[] matrix, double zoom, float scale, boolean clip) { private static int mCount;
// stencil buffer index to start fill
private static int mStart;
static PolygonLayer drawPolygons(final PolygonLayer layer, final int next,
final float[] matrix, final double zoom, final float scale, boolean first) {
glUseProgram(polygonProgram); glUseProgram(polygonProgram);
GLES20.glEnableVertexAttribArray(hPolygonVertexPosition); GLES20.glEnableVertexAttribArray(hPolygonVertexPosition);
@ -160,33 +167,42 @@ class PolygonRenderer {
glUniformMatrix4fv(hPolygonMatrix, 1, false, matrix, 0); glUniformMatrix4fv(hPolygonMatrix, 1, false, matrix, 0);
// use stencilbuffer method for polygon drawing
glEnable(GL_STENCIL_TEST); glEnable(GL_STENCIL_TEST);
PolygonLayer l = layer; PolygonLayer l = layer;
boolean first = clip; if (first) {
int cnt = 0; mCount = 0;
mStart = 0;
} else {
mStart = mCount;
}
for (; l != null && l.layer < next; l = l.next) { for (; l != null && l.layer < next; l = l.next) {
// fade out polygon layers (set in RederTheme) // fade out polygon layers (set in RederTheme)
if (l.area.fade > 0 && l.area.fade > zoom) if (l.area.fade > 0 && l.area.fade > zoom)
continue; continue;
if (cnt == 0) { if (mCount == 0) {
// clear stencilbuffer (tile region)
// disable drawing to framebuffer // disable drawing to framebuffer
glColorMask(false, false, false, false); glColorMask(false, false, false, false);
// never pass the test: always apply fail op // never pass the test: always apply fail op
glStencilFunc(GLES20.GL_ALWAYS, 0, 0xFF); glStencilFunc(GLES20.GL_ALWAYS, 0, 0xFF);
glStencilMask(0xFF); glStencilMask(0xFF);
// glClear(GL_STENCIL_BUFFER_BIT);
// clear stencilbuffer (tile region)
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
if (first) { if (first) {
// draw clip-region into depth buffer // draw clip-region into depth buffer:
// this is used for lines and polygons
// write to depth buffer
GLES20.glDepthMask(true); GLES20.glDepthMask(true);
// to prevent overdraw gl_less restricts // to prevent overdraw gl_less restricts
// the clip to the area where no other // the clip to the area where no other
// tile was drawn // tile was drawn
@ -197,6 +213,7 @@ class PolygonRenderer {
if (first) { if (first) {
first = false; first = false;
// dont modify depth buffer
GLES20.glDepthMask(false); GLES20.glDepthMask(false);
// only draw to this tile // only draw to this tile
GLES20.glDepthFunc(GLES20.GL_EQUAL); GLES20.glDepthFunc(GLES20.GL_EQUAL);
@ -205,29 +222,50 @@ class PolygonRenderer {
// stencil op for stencil method polygon drawing // stencil op for stencil method polygon drawing
glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT); glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
// no need for depth test while drawing stencil
glDisable(GLES20.GL_DEPTH_TEST);
} else if (mCount == mStart) {
// disable drawing to framebuffer
glColorMask(false, false, false, false);
// never pass the test: always apply fail op
glStencilFunc(GLES20.GL_ALWAYS, 0, 0xFF);
// stencil op for stencil method polygon drawing
glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
// no need for depth test while drawing stencil // no need for depth test while drawing stencil
glDisable(GLES20.GL_DEPTH_TEST); glDisable(GLES20.GL_DEPTH_TEST);
} }
mFillPolys[cnt] = l;
mFillPolys[mCount] = l;
// set stencil mask to draw to // set stencil mask to draw to
glStencilMask(1 << cnt++); glStencilMask(1 << mCount++);
glDrawArrays(GL_TRIANGLE_FAN, l.offset, l.verticesCnt); glDrawArrays(GL_TRIANGLE_FAN, l.offset, l.verticesCnt);
// draw up to 8 layers into stencil buffer // draw up to 8 layers into stencil buffer
if (cnt == STENCIL_BITS) { if (mCount == STENCIL_BITS) {
fillPolygons(cnt, zoom, scale); fillPolygons(zoom, scale);
cnt = 0; mCount = 0;
mStart = 0;
} }
} }
if (cnt > 0) if (mCount > 0)
fillPolygons(cnt, zoom, scale); fillPolygons(zoom, scale);
//
// if (mCount > 5){
// mCount = 0;
// mStart = 0;
// }
glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
if (clip && first) if (first)
drawDepthClip(); drawDepthClip();
GLES20.glDisableVertexAttribArray(hPolygonVertexPosition); GLES20.glDisableVertexAttribArray(hPolygonVertexPosition);