- started overlays
- started symbol layer - move renderer and generator out of view package - hopefully the last big refactoring for a while... - improve perspective, plane should be more far away to decrease foreshortening
This commit is contained in:
@@ -17,6 +17,7 @@ package org.oscim.view;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.theme.InternalRenderTheme;
|
||||
|
||||
import android.app.Activity;
|
||||
@@ -24,14 +25,17 @@ import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
|
||||
/**
|
||||
* MapActivity is the abstract base class which must be extended in order to use a {@link MapView}. There are no
|
||||
* abstract methods in this implementation which subclasses need to override and no API key or registration is required.
|
||||
* MapActivity is the abstract base class which must be extended in order to use
|
||||
* a {@link MapView}. There are no abstract methods in this implementation which
|
||||
* subclasses need to override and no API key or registration is required.
|
||||
* <p>
|
||||
* A subclass may create a MapView either via one of the MapView constructors or by inflating an XML layout file. It is
|
||||
* possible to use more than one MapView at the same time.
|
||||
* A subclass may create a MapView either via one of the MapView constructors or
|
||||
* by inflating an XML layout file. It is possible to use more than one MapView
|
||||
* at the same time.
|
||||
* <p>
|
||||
* When the MapActivity is shut down, the current center position, zoom level and map file of the MapView are saved in a
|
||||
* preferences file and restored in the next startup process.
|
||||
* When the MapActivity is shut down, the current center position, zoom level
|
||||
* and map file of the MapView are saved in a preferences file and restored in
|
||||
* the next startup process.
|
||||
*/
|
||||
public abstract class MapActivity extends Activity {
|
||||
private static final String KEY_LATITUDE = "latitude";
|
||||
@@ -88,6 +92,12 @@ public abstract class MapActivity extends Activity {
|
||||
mMapView.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
mMapView.onStop();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called once by each MapView during its setup process.
|
||||
*
|
||||
@@ -104,7 +114,8 @@ public abstract class MapActivity extends Activity {
|
||||
//
|
||||
// if (sharedPreferences.contains(KEY_MAP_FILE)) {
|
||||
// // get and set the map file
|
||||
// mapView.setMapFile(sharedPreferences.getString(KEY_MAP_FILE, null));
|
||||
// mapView.setMapFile(sharedPreferences.getString(KEY_MAP_FILE,
|
||||
// null));
|
||||
// }
|
||||
|
||||
// get and set the map position and zoom level
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* 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 org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
|
||||
import android.opengl.Matrix;
|
||||
|
||||
/**
|
||||
* A MapPosition Container.
|
||||
*/
|
||||
public class MapPosition {
|
||||
|
||||
public double lon;
|
||||
public double lat;
|
||||
|
||||
public byte zoomLevel;
|
||||
public float scale;
|
||||
public float angle;
|
||||
|
||||
public double x;
|
||||
public double y;
|
||||
|
||||
public float[] rotation;
|
||||
|
||||
public MapPosition() {
|
||||
this.zoomLevel = (byte) 1;
|
||||
this.scale = 1;
|
||||
this.lat = 0;
|
||||
this.lon = 0;
|
||||
this.angle = 0;
|
||||
this.x = MercatorProjection.longitudeToPixelX(this.lon, zoomLevel);
|
||||
this.y = MercatorProjection.latitudeToPixelY(this.lat, zoomLevel);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
rotation = new float[16];
|
||||
Matrix.setIdentityM(rotation, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param geoPoint
|
||||
* the map position.
|
||||
* @param zoomLevel
|
||||
* the zoom level.
|
||||
* @param scale
|
||||
* ...
|
||||
*/
|
||||
public MapPosition(GeoPoint geoPoint, byte zoomLevel, float scale) {
|
||||
// this.geoPoint = geoPoint;
|
||||
this.zoomLevel = zoomLevel;
|
||||
this.scale = scale;
|
||||
this.lat = geoPoint.getLatitude();
|
||||
this.lon = geoPoint.getLongitude();
|
||||
this.angle = 0;
|
||||
this.x = MercatorProjection.longitudeToPixelX(this.lon, zoomLevel);
|
||||
this.y = MercatorProjection.latitudeToPixelY(this.lat, zoomLevel);
|
||||
}
|
||||
|
||||
public MapPosition(double latitude, double longitude, byte zoomLevel, float scale,
|
||||
float angle) {
|
||||
this.zoomLevel = zoomLevel;
|
||||
this.scale = scale;
|
||||
this.lat = latitude;
|
||||
this.lon = longitude;
|
||||
this.angle = angle;
|
||||
this.x = MercatorProjection.longitudeToPixelX(longitude, zoomLevel);
|
||||
this.y = MercatorProjection.latitudeToPixelY(latitude, zoomLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("MapPosition [geoPoint=");
|
||||
builder.append("lat");
|
||||
builder.append(this.lat);
|
||||
builder.append("lon");
|
||||
builder.append(this.lon);
|
||||
builder.append(", zoomLevel=");
|
||||
builder.append(this.zoomLevel);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package org.oscim.view;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
@@ -23,22 +23,23 @@ import java.util.Map;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.database.IMapDatabase;
|
||||
import org.oscim.database.MapDatabaseFactory;
|
||||
import org.oscim.database.MapDatabases;
|
||||
import org.oscim.database.MapInfo;
|
||||
import org.oscim.database.OpenResult;
|
||||
import org.oscim.generator.JobQueue;
|
||||
import org.oscim.generator.JobTile;
|
||||
import org.oscim.generator.MapWorker;
|
||||
import org.oscim.renderer.MapRenderer;
|
||||
import org.oscim.renderer.TileGenerator;
|
||||
import org.oscim.theme.ExternalRenderTheme;
|
||||
import org.oscim.theme.InternalRenderTheme;
|
||||
import org.oscim.theme.RenderTheme;
|
||||
import org.oscim.theme.RenderThemeHandler;
|
||||
import org.oscim.theme.Theme;
|
||||
import org.oscim.view.generator.JobQueue;
|
||||
import org.oscim.view.generator.JobTile;
|
||||
import org.oscim.view.generator.MapWorker;
|
||||
import org.oscim.view.renderer.MapRenderer;
|
||||
import org.oscim.view.renderer.TileGenerator;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -139,7 +140,7 @@ public class MapView extends FrameLayout {
|
||||
|
||||
mJobQueue = new JobQueue();
|
||||
|
||||
mMapRenderer = new MapRenderer(context, this);
|
||||
mMapRenderer = MapRenderer.create(context, this);
|
||||
|
||||
mMapWorkers = new MapWorker[mNumMapWorkers];
|
||||
|
||||
@@ -521,6 +522,11 @@ public class MapView extends FrameLayout {
|
||||
mPausing = false;
|
||||
}
|
||||
|
||||
public void onStop() {
|
||||
Log.d(TAG, "onStop");
|
||||
mMapRenderer.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maximum possible zoom level.
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.oscim.view;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.utils.FastMath;
|
||||
|
||||
@@ -26,13 +27,17 @@ import android.util.Log;
|
||||
* A MapPosition stores the latitude and longitude coordinate of a MapView
|
||||
* together with its zoom level.
|
||||
*/
|
||||
|
||||
// TODO use global coordinates that directly scale to pixel
|
||||
|
||||
public class MapViewPosition {
|
||||
|
||||
private static final String TAG = "MapViewPosition";
|
||||
|
||||
public final static int MAX_ZOOMLEVEL = 17;
|
||||
public final static int MIN_ZOOMLEVEL = 2;
|
||||
|
||||
private final static float MAX_ANGLE = 20;
|
||||
private final static float MAX_ANGLE = 35;
|
||||
|
||||
private final MapView mMapView;
|
||||
|
||||
@@ -43,13 +48,11 @@ public class MapViewPosition {
|
||||
private float mScale;
|
||||
// 2^mZoomLevel * mScale;
|
||||
private float mMapScale;
|
||||
|
||||
private float mRotation;
|
||||
public float mTilt;
|
||||
|
||||
MapViewPosition(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
|
||||
mLatitude = Double.NaN;
|
||||
mLongitude = Double.NaN;
|
||||
mZoomLevel = -1;
|
||||
@@ -62,29 +65,41 @@ public class MapViewPosition {
|
||||
private float[] mProjMatrix = new float[16];
|
||||
private float[] mProjMatrixI = new float[16];
|
||||
private float[] mUnprojMatrix = new float[16];
|
||||
private float[] mRotateMatrix = new float[16];
|
||||
private float[] mViewMatrix = new float[16];
|
||||
private float[] mRotMatrix = new float[16];
|
||||
private float[] mTmpMatrix = new float[16];
|
||||
|
||||
private int mHeight, mWidth;
|
||||
public final static float VIEW_SCALE = 1f / 2;
|
||||
public final static float VIEW_DISTANCE = 1;
|
||||
public final static float VIEW_DISTANCE = 2;
|
||||
public final static float VIEW_NEAR = VIEW_DISTANCE;
|
||||
public final static float VIEW_FAR = VIEW_DISTANCE * 2;
|
||||
|
||||
public final static float DIST = 2;
|
||||
|
||||
// public final static float VIEW_SCALE = 1f / 2;
|
||||
// public final static float VIEW_DISTANCE = 1;
|
||||
// public final static float VIEW_NEAR = 1;
|
||||
// public final static float VIEW_FAR = 2;
|
||||
|
||||
void setViewport(int width, int height) {
|
||||
float sw = VIEW_SCALE;
|
||||
float sh = VIEW_SCALE;
|
||||
float aspect = height / (float) width;
|
||||
|
||||
Matrix.frustumM(mProjMatrix, 0, -sw * width, sw * width,
|
||||
sh * height, -sh * height, 1, 2);
|
||||
Matrix.frustumM(mProjMatrix, 0, -1 * sw, 1 * sw,
|
||||
aspect * sh, -aspect * sh, VIEW_NEAR, VIEW_FAR);
|
||||
|
||||
Matrix.translateM(mProjMatrix, 0, 0, 0, -VIEW_DISTANCE);
|
||||
Matrix.setIdentityM(mTmpMatrix, 0);
|
||||
Matrix.translateM(mTmpMatrix, 0, 0, 0, -VIEW_DISTANCE);
|
||||
Matrix.multiplyMM(mProjMatrix, 0, mProjMatrix, 0, mTmpMatrix, 0);
|
||||
|
||||
Matrix.invertM(mProjMatrixI, 0, mProjMatrix, 0);
|
||||
Matrix.invertM(mUnprojMatrix, 0, mProjMatrix, 0);
|
||||
|
||||
Matrix.setIdentityM(mRotateMatrix, 0);
|
||||
|
||||
mHeight = height;
|
||||
mWidth = width;
|
||||
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
public synchronized boolean getMapPosition(final MapPosition mapPosition,
|
||||
@@ -92,60 +107,77 @@ public class MapViewPosition {
|
||||
// if (!isValid())
|
||||
// return false;
|
||||
|
||||
// if (mapPosition.lat == mLatitude
|
||||
// && mapPosition.lon == mLongitude
|
||||
// && mapPosition.zoomLevel == mZoomLevel
|
||||
// && mapPosition.scale == mScale
|
||||
// && mapPosition.angle == mRotation)
|
||||
// return false;
|
||||
if (mapPosition.lat == mLatitude
|
||||
&& mapPosition.lon == mLongitude
|
||||
&& mapPosition.zoomLevel == mZoomLevel
|
||||
&& mapPosition.scale == mScale
|
||||
&& mapPosition.angle == mRotation
|
||||
&& mapPosition.tilt == mTilt)
|
||||
return false;
|
||||
|
||||
byte z = mZoomLevel;
|
||||
|
||||
mapPosition.lat = mLatitude;
|
||||
mapPosition.lon = mLongitude;
|
||||
mapPosition.angle = mRotation;
|
||||
mapPosition.tilt = mTilt;
|
||||
mapPosition.scale = mScale;
|
||||
mapPosition.zoomLevel = z;
|
||||
|
||||
mapPosition.x = MercatorProjection.longitudeToPixelX(mLongitude, z);
|
||||
mapPosition.y = MercatorProjection.latitudeToPixelY(mLatitude, z);
|
||||
|
||||
if (mapPosition.rotation != null) {
|
||||
// updateMatrix();
|
||||
System.arraycopy(mRotateMatrix, 0, mapPosition.rotation, 0, 16);
|
||||
}
|
||||
if (mapPosition.viewMatrix != null)
|
||||
System.arraycopy(mViewMatrix, 0, mapPosition.viewMatrix, 0, 16);
|
||||
|
||||
if (mapPosition.rotateMatrix != null)
|
||||
System.arraycopy(mRotMatrix, 0, mapPosition.rotateMatrix, 0, 16);
|
||||
|
||||
if (coords == null)
|
||||
return true;
|
||||
|
||||
// if (mapPosition.rotation == null)
|
||||
// updateMatrix();
|
||||
// not so sure about this, but somehow works. weird z-values...
|
||||
float tilt = FloatMath.sin((float) Math.toRadians(mTilt
|
||||
// * 2.2f for dist = 1
|
||||
* 1.4f // for dist = 2
|
||||
// * 0.8f for dist = 4
|
||||
* ((float) mHeight / mWidth)));
|
||||
|
||||
// not so sure about this, but works...
|
||||
float tilt = (float) Math.sin(Math.toRadians(mTilt)) * 4;
|
||||
|
||||
unproject(-1, 1, tilt, coords, 0); // top-left
|
||||
unproject(1, 1, tilt, coords, 2); // top-right
|
||||
unproject(1, -1, -tilt, coords, 4); // bottom-right
|
||||
unproject(-1, -1, -tilt, coords, 6); // bottom-left
|
||||
float d = 1f;
|
||||
unproject(-d, d, tilt, coords, 0); // bottom-left
|
||||
unproject(d, d, tilt, coords, 2); // bottom-right
|
||||
unproject(d, -d, -tilt, coords, 4); // top-right
|
||||
unproject(-d, -d, -tilt, coords, 6); // top-left
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private float[] mv = { 0, 0, 0, 1 };
|
||||
// private float[] mu = { 0, 0, 0, 1 };
|
||||
private float[] mBBoxCoords = new float[8];
|
||||
|
||||
private void unproject(float x, float y, float z, float[] coords, int position) {
|
||||
// mv[0] = x;
|
||||
// mv[1] = y;
|
||||
// mv[2] = z - 2f;
|
||||
// // mv[2] = 1f / (z - 2f);
|
||||
// mv[3] = 1;
|
||||
// Matrix.multiplyMV(mu, 0, mProjMatrix, 0, mv, 0);
|
||||
|
||||
mv[0] = x;
|
||||
mv[1] = y;
|
||||
mv[2] = z - VIEW_DISTANCE;
|
||||
mv[2] = z - 1f;
|
||||
// mv[2] = -mu[2] / mu[3];
|
||||
mv[3] = 1;
|
||||
|
||||
Matrix.multiplyMV(mv, 0, mUnprojMatrix, 0, mv, 0);
|
||||
|
||||
if (mv[3] != 0) {
|
||||
float w = 1 / mv[3];
|
||||
coords[position] = mv[0] * w;
|
||||
coords[position + 1] = mv[1] * w;
|
||||
coords[position] = mv[0] / mv[3];
|
||||
coords[position + 1] = mv[1] / mv[3];
|
||||
// Log.d(TAG, (z * 1.4f - 1) + " " + mu[2] / mu[3] + " - " + x + ":"
|
||||
// + y + " - "
|
||||
// + coords[position] + ":" + coords[position + 1] + " - " + mTilt);
|
||||
} else {
|
||||
// else what?
|
||||
Log.d(TAG, "... what?");
|
||||
@@ -153,23 +185,50 @@ public class MapViewPosition {
|
||||
}
|
||||
|
||||
private void updateMatrix() {
|
||||
Matrix.setRotateM(mRotateMatrix, 0, mRotation, 0, 0, 1);
|
||||
Matrix.setRotateM(mRotMatrix, 0, mRotation, 0, 0, 1);
|
||||
// - view matrix
|
||||
// 1. scale to window coordinates
|
||||
// 2. rotate
|
||||
// 3. tilt
|
||||
|
||||
// - projection matrix
|
||||
// 4. translate to near-plane
|
||||
// 5. apply projection
|
||||
|
||||
// tilt map
|
||||
float tilt = mTilt;
|
||||
Matrix.setRotateM(mTmpMatrix, 0, tilt / (mHeight / 2), 1, 0, 0);
|
||||
Matrix.setRotateM(mTmpMatrix, 0, tilt, 1, 0, 0);
|
||||
|
||||
// apply first rotation, then tilt
|
||||
Matrix.multiplyMM(mRotateMatrix, 0, mTmpMatrix, 0, mRotateMatrix, 0);
|
||||
// apply first viewMatrix, then tilt
|
||||
Matrix.multiplyMM(mRotMatrix, 0, mTmpMatrix, 0, mRotMatrix, 0);
|
||||
|
||||
// scale to window coordinates
|
||||
Matrix.setIdentityM(mTmpMatrix, 0);
|
||||
Matrix.scaleM(mTmpMatrix, 0, 1f / mWidth, 1f / mWidth, 1);
|
||||
|
||||
Matrix.multiplyMM(mViewMatrix, 0, mRotMatrix, 0, mTmpMatrix, 0);
|
||||
|
||||
// // move to near plane
|
||||
// Matrix.setIdentityM(mTmpMatrix, 0);
|
||||
// Matrix.translateM(mTmpMatrix, 0, 0, 0, -VIEW_DISTANCE);
|
||||
// Matrix.multiplyMM(mViewMatrix, 0, mTmpMatrix, 0, mViewMatrix, 0);
|
||||
|
||||
// get unproject matrix:
|
||||
// (transpose of rotation is its inverse)
|
||||
Matrix.transposeM(mTmpMatrix, 0, mRotateMatrix, 0);
|
||||
// Matrix.invertM(mTmpMatrix, 0, mRotateMatrix, 0);
|
||||
// Matrix.invertM(mTmpMatrix, 0, mViewMatrix, 0);
|
||||
Matrix.setIdentityM(mUnprojMatrix, 0);
|
||||
|
||||
// inverse scale
|
||||
Matrix.scaleM(mUnprojMatrix, 0, mWidth, mWidth, 1);
|
||||
|
||||
// inverse rotation
|
||||
Matrix.transposeM(mTmpMatrix, 0, mRotMatrix, 0);
|
||||
|
||||
// (AB)^-1 = B^-1*A^-1
|
||||
Matrix.multiplyMM(mUnprojMatrix, 0, mTmpMatrix, 0, mProjMatrixI, 0);
|
||||
Matrix.multiplyMM(mTmpMatrix, 0, mUnprojMatrix, 0, mTmpMatrix, 0);
|
||||
|
||||
// unapply projection, tilt, rotate and scale
|
||||
// (AB)^-1 = B^-1*A^-1
|
||||
Matrix.multiplyMM(mUnprojMatrix, 0, mTmpMatrix, 0, mProjMatrixI, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,7 +239,7 @@ public class MapViewPosition {
|
||||
*/
|
||||
public synchronized void getViewBox(final float[] viewBox) {
|
||||
|
||||
updateMatrix();
|
||||
// updateMatrix();
|
||||
|
||||
float tilt = FloatMath.sin((float) Math.toRadians(mTilt)) * 4;
|
||||
|
||||
@@ -383,13 +442,27 @@ public class MapViewPosition {
|
||||
if (mTilt == tilt)
|
||||
return false;
|
||||
|
||||
mTilt = tilt;
|
||||
updateMatrix();
|
||||
setTilt(tilt);
|
||||
// mTilt = tilt;
|
||||
// updateMatrix();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setTilt(float f) {
|
||||
mTilt = f;
|
||||
|
||||
// float sw = VIEW_SCALE;
|
||||
// float sh = VIEW_SCALE;
|
||||
// sh += (mTilt / 250);
|
||||
|
||||
// Matrix.frustumM(mProjMatrix, 0, -sw * mWidth, sw * mWidth,
|
||||
// sh * mHeight, -sh * mHeight, 1, 2);
|
||||
//
|
||||
// Matrix.translateM(mProjMatrix, 0, 0, 0, -VIEW_DISTANCE);
|
||||
//
|
||||
// Matrix.invertM(mProjMatrixI, 0, mProjMatrix, 0);
|
||||
// Matrix.invertM(mUnprojMatrix, 0, mProjMatrix, 0);
|
||||
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
*/
|
||||
package org.oscim.view;
|
||||
|
||||
import org.oscim.view.renderer.TileGenerator;
|
||||
import org.oscim.renderer.TileGenerator;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.ArrayList;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* 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.generator;
|
||||
|
||||
//import static org.oscim.view.mapgenerator.JobTile.LOADING;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
/**
|
||||
* A JobQueue keeps the list of pending jobs for a MapView and prioritizes them.
|
||||
*/
|
||||
public class JobQueue {
|
||||
private static final int INITIAL_CAPACITY = 64;
|
||||
|
||||
private PriorityQueue<JobTile> mPriorityQueue;
|
||||
|
||||
/**
|
||||
*/
|
||||
public JobQueue() {
|
||||
mPriorityQueue = new PriorityQueue<JobTile>(INITIAL_CAPACITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tiles
|
||||
* the job to be added to this queue.
|
||||
*/
|
||||
public synchronized void setJobs(ArrayList<JobTile> tiles) {
|
||||
mPriorityQueue.clear();
|
||||
// mPriorityQueue.addAll(tiles);
|
||||
for (int i = 0, n = tiles.size(); i < n; i++) {
|
||||
JobTile tile = tiles.get(i);
|
||||
// tile.state = LOADING;
|
||||
tile.isLoading = true;
|
||||
mPriorityQueue.offer(tile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all jobs from this queue.
|
||||
*/
|
||||
public synchronized void clear() {
|
||||
for (int i = 0, n = mPriorityQueue.size(); i < n; i++) {
|
||||
JobTile tile = mPriorityQueue.poll();
|
||||
// tile.state = 0;
|
||||
tile.isLoading = false;
|
||||
}
|
||||
mPriorityQueue.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this queue contains no jobs, false otherwise.
|
||||
*/
|
||||
public synchronized boolean isEmpty() {
|
||||
return mPriorityQueue.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the most important job from this queue or null, if empty.
|
||||
*/
|
||||
public synchronized JobTile poll() {
|
||||
JobTile tile = mPriorityQueue.poll();
|
||||
|
||||
return tile;
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 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.generator;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class JobTile extends Tile implements Comparable<JobTile> {
|
||||
// public final static int LOADING = 1;
|
||||
// public final static int NEWDATA = 1 << 1;
|
||||
// public final static int READY = 1 << 2;
|
||||
// public final static int AVAILABLE = 1 << 1 | 1 << 2;
|
||||
// public final static int CANCELED = 1 << 3;
|
||||
// public int state;
|
||||
|
||||
/**
|
||||
* tile is in JobQueue
|
||||
*/
|
||||
public boolean isLoading;
|
||||
|
||||
/**
|
||||
* distance from map center.
|
||||
*/
|
||||
public float distance;
|
||||
|
||||
/**
|
||||
* @param tileX
|
||||
* ...
|
||||
* @param tileY
|
||||
* ...
|
||||
* @param zoomLevel
|
||||
* ..
|
||||
*/
|
||||
public JobTile(int tileX, int tileY, byte zoomLevel) {
|
||||
super(tileX, tileY, zoomLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(JobTile o) {
|
||||
if (this.distance < o.distance) {
|
||||
return -1;
|
||||
}
|
||||
if (this.distance > o.distance) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* 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.generator;
|
||||
|
||||
import org.oscim.utils.PausableThread;
|
||||
import org.oscim.view.renderer.MapRenderer;
|
||||
import org.oscim.view.renderer.TileGenerator;
|
||||
|
||||
/**
|
||||
* A MapWorker uses a {@link TileGenerator} to generate map tiles. It runs in a
|
||||
* separate thread to avoid blocking the UI thread.
|
||||
*/
|
||||
public class MapWorker extends PausableThread {
|
||||
private final String THREAD_NAME;
|
||||
private final JobQueue mJobQueue;
|
||||
private final TileGenerator mMapGenerator;
|
||||
private final MapRenderer mMapRenderer;
|
||||
|
||||
/**
|
||||
* @param id
|
||||
* thread id
|
||||
* @param jobQueue
|
||||
* ...
|
||||
* @param tileGenerator
|
||||
* ...
|
||||
* @param mapRenderer
|
||||
* ...
|
||||
*/
|
||||
public MapWorker(int id, JobQueue jobQueue, TileGenerator tileGenerator,
|
||||
MapRenderer mapRenderer) {
|
||||
super();
|
||||
mJobQueue = jobQueue;
|
||||
mMapGenerator = tileGenerator;
|
||||
mMapRenderer = mapRenderer;
|
||||
|
||||
THREAD_NAME = "MapWorker" + id;
|
||||
}
|
||||
|
||||
public TileGenerator getMapGenerator() {
|
||||
return mMapGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterRun() {
|
||||
// empty
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWork() {
|
||||
JobTile tile = mJobQueue.poll();
|
||||
|
||||
if (mMapGenerator == null || tile == null)
|
||||
return;
|
||||
|
||||
boolean success = mMapGenerator.executeJob(tile);
|
||||
|
||||
if (!isInterrupted() && success) {
|
||||
mMapRenderer.passTile(tile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getThreadName() {
|
||||
return THREAD_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void takeabreak() {
|
||||
mMapGenerator.getMapDatabase().cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getThreadPriority() {
|
||||
return (Thread.NORM_PRIORITY + Thread.MIN_PRIORITY) / 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasWork() {
|
||||
return !mJobQueue.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* 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.generator;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class TileDistanceSort implements Comparator<JobTile> {
|
||||
|
||||
@Override
|
||||
public int compare(JobTile tile1, JobTile tile2) {
|
||||
if (tile1.distance == tile2.distance)
|
||||
return 0;
|
||||
|
||||
return tile1.distance > tile2.distance ? 1 : -1;
|
||||
}
|
||||
}
|
||||
@@ -1,933 +0,0 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.renderer;
|
||||
|
||||
import static android.opengl.GLES20.GL_ARRAY_BUFFER;
|
||||
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_ONE;
|
||||
import static android.opengl.GLES20.GL_ONE_MINUS_SRC_ALPHA;
|
||||
import static android.opengl.GLES20.GL_POLYGON_OFFSET_FILL;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.theme.RenderTheme;
|
||||
import org.oscim.utils.GlUtils;
|
||||
import org.oscim.view.MapPosition;
|
||||
import org.oscim.view.MapView;
|
||||
import org.oscim.view.MapViewPosition;
|
||||
import org.oscim.view.renderer.MapRenderer.TilesData;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.opengl.Matrix;
|
||||
import android.os.SystemClock;
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
|
||||
private static final String TAG = "SurfaceRenderer";
|
||||
|
||||
private static final int MB = 1024 * 1024;
|
||||
private static final int SHORT_BYTES = 2;
|
||||
private static final int CACHE_TILES_MAX = 250;
|
||||
private static final int LIMIT_BUFFERS = 16 * MB;
|
||||
|
||||
static final float COORD_MULTIPLIER = 8.0f;
|
||||
|
||||
static int CACHE_TILES = CACHE_TILES_MAX;
|
||||
|
||||
private final MapView mMapView;
|
||||
private final MapViewPosition mMapViewPosition;
|
||||
|
||||
private static MapPosition mMapPosition;
|
||||
|
||||
private static ArrayList<VertexBufferObject> mVBOs;
|
||||
|
||||
private static int mWidth, mHeight;
|
||||
|
||||
private static int rotateBuffers = 2;
|
||||
private static ShortBuffer shortBuffer[];
|
||||
private static short[] mFillCoords;
|
||||
|
||||
// bytes currently loaded in VBOs
|
||||
private static int mBufferMemoryUsage;
|
||||
|
||||
private static float[] mMVPMatrix = new float[16];
|
||||
private static float[] mProjMatrix = new float[16];
|
||||
private static float[] mTileCoords = new float[8];
|
||||
|
||||
// mNextTiles is set by TileLoader and swapped with
|
||||
// mDrawTiles in onDrawFrame in GL thread.
|
||||
private static TilesData mNextTiles;
|
||||
/* package */static TilesData mDrawTiles;
|
||||
|
||||
// flag set by updateVisibleList when current visible tiles
|
||||
// changed. used in onDrawFrame to flip mNextTiles/mDrawTiles
|
||||
private static boolean mUpdateTiles;
|
||||
|
||||
private float[] mClearColor = null;
|
||||
|
||||
// number of tiles drawn in one frame
|
||||
private static short mDrawCount = 0;
|
||||
|
||||
private static boolean mUpdateColor = false;
|
||||
|
||||
// drawlock to synchronize Main- and GL-Thread
|
||||
static ReentrantLock tilelock = new ReentrantLock();
|
||||
static ReentrantLock drawlock = new ReentrantLock();
|
||||
|
||||
/* package */static int mHolderCount;
|
||||
|
||||
// scanline fill class used to check tile visibility
|
||||
private static ScanBox mScanBox = new ScanBox() {
|
||||
@Override
|
||||
void setVisible(int y, int x1, int x2) {
|
||||
int cnt = mDrawTiles.cnt;
|
||||
|
||||
MapTile[] tiles = mDrawTiles.tiles;
|
||||
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
MapTile t = tiles[i];
|
||||
if (t.tileY == y && t.tileX >= x1 && t.tileX < x2)
|
||||
t.isVisible = true;
|
||||
}
|
||||
|
||||
int xmax = 1 << mZoom;
|
||||
if (x1 >= 0 && x2 < xmax)
|
||||
return;
|
||||
|
||||
// add placeholder tiles to show both sides
|
||||
// of date line...
|
||||
for (int x = x1; x < x2; x++) {
|
||||
MapTile holder = null;
|
||||
MapTile tile = null;
|
||||
boolean found = false;
|
||||
|
||||
int xx = x;
|
||||
|
||||
if (x >= 0 && x < xmax)
|
||||
continue;
|
||||
|
||||
if (x < 0)
|
||||
xx = xmax + x;
|
||||
else
|
||||
xx = x - xmax;
|
||||
|
||||
if (xx < 0 || xx >= xmax)
|
||||
continue;
|
||||
|
||||
for (int i = cnt; i < cnt + mHolderCount; i++)
|
||||
if (tiles[i].tileX == x && tiles[i].tileY == y) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < cnt; i++)
|
||||
if (tiles[i].tileX == xx && tiles[i].tileY == y) {
|
||||
tile = tiles[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if (tile == null)
|
||||
continue;
|
||||
|
||||
// Log.d(TAG, "add placeholder " + y + " " + x + ">>" + xx + " "
|
||||
// + tile);
|
||||
|
||||
holder = new MapTile(x, y, mZoom);
|
||||
holder.isVisible = true;
|
||||
holder.holder = tile;
|
||||
tiles[cnt + mHolderCount++] = holder;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param mapView
|
||||
* the MapView
|
||||
*/
|
||||
public GLRenderer(MapView mapView) {
|
||||
Log.d(TAG, "init MapRenderer");
|
||||
|
||||
mMapView = mapView;
|
||||
mMapViewPosition = mapView.getMapViewPosition();
|
||||
mMapPosition = new MapPosition();
|
||||
mMapPosition.init();
|
||||
|
||||
Matrix.setIdentityM(mMVPMatrix, 0);
|
||||
|
||||
// add half pixel to tile clip/fill coordinates to avoid rounding issues
|
||||
short min = -4;
|
||||
short max = (short) ((Tile.TILE_SIZE << 3) + 4);
|
||||
mFillCoords = new short[8];
|
||||
mFillCoords[0] = min;
|
||||
mFillCoords[1] = max;
|
||||
mFillCoords[2] = max;
|
||||
mFillCoords[3] = max;
|
||||
mFillCoords[4] = min;
|
||||
mFillCoords[5] = min;
|
||||
mFillCoords[6] = max;
|
||||
mFillCoords[7] = min;
|
||||
|
||||
shortBuffer = new ShortBuffer[rotateBuffers];
|
||||
|
||||
for (int i = 0; i < rotateBuffers; i++) {
|
||||
ByteBuffer bbuf = ByteBuffer.allocateDirect(MB >> 2)
|
||||
.order(ByteOrder.nativeOrder());
|
||||
|
||||
shortBuffer[i] = bbuf.asShortBuffer();
|
||||
shortBuffer[i].put(mFillCoords, 0, 8);
|
||||
}
|
||||
|
||||
mUpdateTiles = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by TileLoader when list of active tiles changed. the list is
|
||||
* copied to mNextTiles to be used in next call to onDrawFrame
|
||||
*
|
||||
* @param tiles
|
||||
* active tiles
|
||||
*/
|
||||
static void updateTiles(TilesData tiles) {
|
||||
|
||||
MapTile[] newTiles = tiles.tiles;
|
||||
|
||||
// lock tiles (and their proxies) to not be removed from cache
|
||||
for (int i = 0, n = tiles.cnt; i < n; i++)
|
||||
newTiles[i].lock();
|
||||
|
||||
// dont flip next/drawTiles while copying
|
||||
GLRenderer.tilelock.lock();
|
||||
|
||||
MapTile[] nextTiles = mNextTiles.tiles;
|
||||
|
||||
// unlock previously active tiles
|
||||
for (int i = 0, n = mNextTiles.cnt; i < n; i++)
|
||||
nextTiles[i].unlock();
|
||||
|
||||
// copy newTiles to nextTiles
|
||||
System.arraycopy(newTiles, 0, nextTiles, 0, tiles.cnt);
|
||||
|
||||
mNextTiles.cnt = tiles.cnt;
|
||||
|
||||
// flip next/drawTiles in next onDrawFrame
|
||||
mUpdateTiles = true;
|
||||
|
||||
GLRenderer.tilelock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* called by TileLoader. when tile is removed from cache reuse its vbo.
|
||||
*
|
||||
* @param vbo
|
||||
* the VBO
|
||||
*/
|
||||
static void addVBO(VertexBufferObject vbo) {
|
||||
synchronized (mVBOs) {
|
||||
mVBOs.add(vbo);
|
||||
}
|
||||
}
|
||||
|
||||
void setVBO(MapTile tile) {
|
||||
synchronized (mVBOs) {
|
||||
int numVBOs = mVBOs.size();
|
||||
|
||||
if (numVBOs > 0 && tile.vbo == null) {
|
||||
tile.vbo = mVBOs.remove(numVBOs - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setRenderTheme(RenderTheme t) {
|
||||
int bg = t.getMapBackground();
|
||||
float[] c = new float[4];
|
||||
c[3] = (bg >> 24 & 0xff) / 255.0f;
|
||||
c[0] = (bg >> 16 & 0xff) / 255.0f;
|
||||
c[1] = (bg >> 8 & 0xff) / 255.0f;
|
||||
c[2] = (bg >> 0 & 0xff) / 255.0f;
|
||||
mClearColor = c;
|
||||
mUpdateColor = true;
|
||||
}
|
||||
|
||||
private int uploadCnt = 0;
|
||||
|
||||
private boolean uploadTileData(MapTile tile) {
|
||||
// Upload line data to vertex buffer object
|
||||
// Log.d(TAG, "uploadTileData, " + tile);
|
||||
|
||||
int lineSize = LineRenderer.sizeOf(tile.lineLayers);
|
||||
int polySize = PolygonRenderer.sizeOf(tile.polygonLayers);
|
||||
int newSize = lineSize + polySize;
|
||||
|
||||
if (newSize == 0) {
|
||||
LineRenderer.clear(tile.lineLayers);
|
||||
PolygonRenderer.clear(tile.polygonLayers);
|
||||
tile.lineLayers = null;
|
||||
tile.polygonLayers = null;
|
||||
tile.newData = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
GLES20.glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id);
|
||||
|
||||
// use multiple buffers to avoid overwriting buffer while current
|
||||
// data is uploaded (or rather the blocking which is probably done to
|
||||
// avoid overwriting)
|
||||
if (uploadCnt >= rotateBuffers) {
|
||||
uploadCnt = 0;
|
||||
// GLES20.glFlush();
|
||||
}
|
||||
|
||||
ShortBuffer sbuf = shortBuffer[uploadCnt];
|
||||
|
||||
// add fill coordinates
|
||||
newSize += 8;
|
||||
|
||||
// probably not a good idea to do this in gl thread...
|
||||
if (sbuf.capacity() < newSize) {
|
||||
ByteBuffer bbuf = ByteBuffer.allocateDirect(newSize * SHORT_BYTES)
|
||||
.order(ByteOrder.nativeOrder());
|
||||
sbuf = bbuf.asShortBuffer();
|
||||
shortBuffer[uploadCnt] = sbuf;
|
||||
sbuf.put(mFillCoords, 0, 8);
|
||||
}
|
||||
|
||||
sbuf.clear();
|
||||
sbuf.position(8);
|
||||
|
||||
PolygonRenderer.compileLayerData(tile.polygonLayers, sbuf);
|
||||
|
||||
tile.lineOffset = (8 + polySize);
|
||||
if (tile.lineOffset != sbuf.position())
|
||||
Log.d(TAG, "tiles lineoffset is wrong: " + tile + " "
|
||||
+ tile.lineOffset + " "
|
||||
+ sbuf.position() + " "
|
||||
+ sbuf.limit() + " "
|
||||
+ sbuf.remaining() + " "
|
||||
+ PolygonRenderer.sizeOf(tile.polygonLayers) + " "
|
||||
+ tile.rel);
|
||||
|
||||
tile.lineOffset *= SHORT_BYTES;
|
||||
|
||||
LineRenderer.compileLayerData(tile.lineLayers, sbuf);
|
||||
|
||||
sbuf.flip();
|
||||
|
||||
if (newSize != sbuf.remaining()) {
|
||||
Log.d(TAG, "tiles wrong: " + tile + " "
|
||||
+ newSize + " "
|
||||
+ sbuf.position() + " "
|
||||
+ sbuf.limit() + " "
|
||||
+ sbuf.remaining() + " "
|
||||
+ LineRenderer.sizeOf(tile.lineLayers)
|
||||
+ tile.isLoading + " "
|
||||
+ tile.rel);
|
||||
|
||||
tile.newData = false;
|
||||
return false;
|
||||
}
|
||||
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
|
||||
&& mBufferMemoryUsage < LIMIT_BUFFERS) {
|
||||
GLES20.glBufferSubData(GL_ARRAY_BUFFER, 0, newSize, sbuf);
|
||||
} else {
|
||||
mBufferMemoryUsage -= tile.vbo.size;
|
||||
tile.vbo.size = newSize;
|
||||
GLES20.glBufferData(GL_ARRAY_BUFFER, tile.vbo.size, sbuf, GL_DYNAMIC_DRAW);
|
||||
mBufferMemoryUsage += tile.vbo.size;
|
||||
}
|
||||
|
||||
uploadCnt++;
|
||||
|
||||
tile.isReady = true;
|
||||
tile.newData = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void checkBufferUsage() {
|
||||
if (mBufferMemoryUsage < LIMIT_BUFFERS) {
|
||||
if (CACHE_TILES < CACHE_TILES_MAX)
|
||||
CACHE_TILES += 50;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 mRotate = false;
|
||||
|
||||
private static void setMatrix(float[] matrix, MapTile tile,
|
||||
float div, boolean project) {
|
||||
MapPosition mapPosition = mMapPosition;
|
||||
|
||||
float x = (float) (tile.pixelX - mapPosition.x * div);
|
||||
float y = (float) (tile.pixelY - mapPosition.y * div);
|
||||
float scale = mapPosition.scale / div;
|
||||
|
||||
Matrix.setIdentityM(matrix, 0);
|
||||
|
||||
// translate relative to map center
|
||||
matrix[12] = x * scale;
|
||||
matrix[13] = y * scale;
|
||||
|
||||
// scale to tile to world coordinates
|
||||
scale /= COORD_MULTIPLIER;
|
||||
matrix[0] = scale;
|
||||
matrix[5] = scale;
|
||||
|
||||
if (mRotate)
|
||||
Matrix.multiplyMM(matrix, 0, mapPosition.rotation, 0, matrix, 0);
|
||||
|
||||
if (project)
|
||||
Matrix.multiplyMM(matrix, 0, mProjMatrix, 0, matrix, 0);
|
||||
|
||||
}
|
||||
|
||||
private static float scaleDiv(MapTile t) {
|
||||
float div = 1;
|
||||
int diff = mMapPosition.zoomLevel - t.zoomLevel;
|
||||
if (diff < 0)
|
||||
div = (1 << -diff);
|
||||
else if (diff > 0)
|
||||
div = (1.0f / (1 << diff));
|
||||
return div;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawFrame(GL10 glUnused) {
|
||||
long start = 0;
|
||||
// prevent main thread recreating all tiles (updateMap)
|
||||
// while rendering is going. not have seen this happen
|
||||
// yet though
|
||||
GLRenderer.drawlock.lock();
|
||||
|
||||
if (MapView.debugFrameTime)
|
||||
start = SystemClock.uptimeMillis();
|
||||
|
||||
if (mUpdateColor) {
|
||||
float cc[] = mClearColor;
|
||||
GLES20.glClearColor(cc[0], cc[1], cc[2], cc[3]);
|
||||
mUpdateColor = false;
|
||||
}
|
||||
|
||||
// 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.glStencilMask(0xFF);
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT
|
||||
| GLES20.GL_DEPTH_BUFFER_BIT
|
||||
| GLES20.GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
// get current tiles to draw
|
||||
if (mUpdateTiles) {
|
||||
GLRenderer.tilelock.lock();
|
||||
TilesData tmp = mDrawTiles;
|
||||
mDrawTiles = mNextTiles;
|
||||
mNextTiles = tmp;
|
||||
mUpdateTiles = false;
|
||||
GLRenderer.tilelock.unlock();
|
||||
}
|
||||
|
||||
if (mDrawTiles == null || mDrawTiles.cnt == 0) {
|
||||
GLRenderer.drawlock.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
mRotate = mMapView.enableRotation || mMapView.enableCompass;
|
||||
|
||||
// get current MapPosition, set mTileCoords (mapping of screen to model
|
||||
// coordinates)
|
||||
MapPosition mapPosition = mMapPosition;
|
||||
float[] coords = mTileCoords;
|
||||
boolean changed = mMapViewPosition.getMapPosition(mapPosition, coords);
|
||||
|
||||
int tileCnt = mDrawTiles.cnt;
|
||||
MapTile[] tiles = mDrawTiles.tiles;
|
||||
|
||||
if (changed) {
|
||||
// get visible tiles
|
||||
for (int i = 0; i < tileCnt; i++)
|
||||
tiles[i].isVisible = false;
|
||||
|
||||
// relative zoom-level, 'tiles' could not have been updated after
|
||||
// zoom-level changed.
|
||||
float div = scaleDiv(tiles[0]);
|
||||
|
||||
float s = Tile.TILE_SIZE;
|
||||
float scale = mapPosition.scale / div;
|
||||
float px = (float) mapPosition.x * div;
|
||||
float py = (float) mapPosition.y * div;
|
||||
|
||||
for (int i = 0; i < 8; i += 2) {
|
||||
coords[i + 0] = (px + coords[i + 0] / scale) / s;
|
||||
coords[i + 1] = (py + coords[i + 1] / scale) / s;
|
||||
}
|
||||
|
||||
mHolderCount = 0;
|
||||
mScanBox.scan(coords, tiles[0].zoomLevel);
|
||||
tileCnt += mHolderCount;
|
||||
|
||||
// // TODO get the right function: trying to accomodate for the
|
||||
// y-stretching introduced by the perspective transformation...
|
||||
// float sw, sh;
|
||||
// sw = MapViewPosition.VIEW_SCALE;
|
||||
// sh = MapViewPosition.VIEW_SCALE;
|
||||
// sh += (mMapViewPosition.mTilt / 150);
|
||||
//
|
||||
// Matrix.frustumM(mProjMatrix, 0, -sw * mWidth, sw * mWidth,
|
||||
// sh * mHeight, -sh * mHeight, 1, 2);
|
||||
//
|
||||
// Matrix.translateM(mProjMatrix, 0, 0, 0,
|
||||
// -MapViewPosition.VIEW_DISTANCE);
|
||||
//
|
||||
// mProjMatrix[10] = 0;
|
||||
// mProjMatrix[14] = 0;
|
||||
}
|
||||
|
||||
uploadCnt = 0;
|
||||
int updateTextures = 0;
|
||||
|
||||
// check visible tiles, upload new vertex data
|
||||
for (int i = 0; i < tileCnt; i++) {
|
||||
MapTile tile = tiles[i];
|
||||
|
||||
// if (!isVisible(mapPosition, tile))
|
||||
// continue;
|
||||
if (!tile.isVisible)
|
||||
continue;
|
||||
|
||||
if (MapView.staticLabeling) {
|
||||
if (tile.texture == null && TextRenderer.drawToTexture(tile))
|
||||
updateTextures++;
|
||||
}
|
||||
|
||||
if (tile.newData) {
|
||||
uploadTileData(tile);
|
||||
continue;
|
||||
}
|
||||
if (tile.holder != null) {
|
||||
if (tile.holder.newData) {
|
||||
uploadTileData(tile.holder);
|
||||
}
|
||||
tile.isReady = tile.holder.isReady;
|
||||
} else if (!tile.isReady) {
|
||||
// check near relatives if they can serve as proxy
|
||||
MapTile rel = tile.rel.parent.tile;
|
||||
if (rel != null && rel.newData) {
|
||||
uploadTileData(rel);
|
||||
} else {
|
||||
for (int c = 0; c < 4; c++) {
|
||||
if (tile.rel.child[c] == null)
|
||||
continue;
|
||||
|
||||
rel = tile.rel.child[c].tile;
|
||||
if (rel != null && rel.newData)
|
||||
uploadTileData(rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (uploadCnt > 0)
|
||||
checkBufferUsage();
|
||||
|
||||
if (MapView.staticLabeling) {
|
||||
if (updateTextures > 0)
|
||||
TextRenderer.compileTextures();
|
||||
}
|
||||
|
||||
GLES20.glEnable(GL_DEPTH_TEST);
|
||||
GLES20.glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
|
||||
for (int i = 0; i < tileCnt; i++) {
|
||||
MapTile t = tiles[i];
|
||||
if (t.isVisible && t.isReady)
|
||||
drawTile(t);
|
||||
}
|
||||
|
||||
// proxies are clipped to the region where nothing was drawn to depth
|
||||
// buffer. TODO draw all parent before grandparent
|
||||
for (int i = 0; i < tileCnt; i++) {
|
||||
MapTile t = tiles[i];
|
||||
if (t.isVisible && !t.isReady && (t.holder == null))
|
||||
drawProxyTile(t);
|
||||
}
|
||||
// GlUtils.checkGlError("end draw");
|
||||
|
||||
GLES20.glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
GLES20.glDisable(GL_DEPTH_TEST);
|
||||
|
||||
mDrawCount = 0;
|
||||
mDrawSerial++;
|
||||
|
||||
if (MapView.staticLabeling) {
|
||||
GLES20.glEnable(GL_BLEND);
|
||||
int z = mapPosition.zoomLevel;
|
||||
float s = mapPosition.scale;
|
||||
|
||||
int zoomLevelDiff = Math.max(z - TileGenerator.STROKE_MAX_ZOOM_LEVEL, 0);
|
||||
float scale = (float) Math.pow(1.4, zoomLevelDiff);
|
||||
if (scale < 1)
|
||||
scale = 1;
|
||||
|
||||
if (z >= TileGenerator.STROKE_MAX_ZOOM_LEVEL)
|
||||
TextRenderer.beginDraw(scale / FloatMath.sqrt(s), mProjMatrix);
|
||||
else
|
||||
TextRenderer.beginDraw(1 / s, mProjMatrix);
|
||||
|
||||
for (int i = 0; i < tileCnt; i++) {
|
||||
MapTile t = tiles[i];
|
||||
if (!t.isVisible)
|
||||
continue;
|
||||
|
||||
if (t.holder == null) {
|
||||
if (t.texture != null) {
|
||||
setMatrix(mMVPMatrix, t, 1, false);
|
||||
TextRenderer.drawTile(t, mMVPMatrix);
|
||||
}
|
||||
} else {
|
||||
if (t.holder.texture != null) {
|
||||
setMatrix(mMVPMatrix, t, 1, false);
|
||||
TextRenderer.drawTile(t.holder, mMVPMatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
TextRenderer.endDraw();
|
||||
}
|
||||
|
||||
// TODO call overlay renderer here
|
||||
|
||||
if (MapView.debugFrameTime) {
|
||||
GLES20.glFinish();
|
||||
Log.d(TAG, "draw took " + (SystemClock.uptimeMillis() - start));
|
||||
}
|
||||
GLRenderer.drawlock.unlock();
|
||||
}
|
||||
|
||||
// used to not draw a tile twice per frame.
|
||||
private static int mDrawSerial = 0;
|
||||
|
||||
private static void drawTile(MapTile tile) {
|
||||
// draw parents only once
|
||||
if (tile.lastDraw == mDrawSerial)
|
||||
return;
|
||||
|
||||
float div = scaleDiv(tile);
|
||||
float[] mvp = mMVPMatrix;
|
||||
MapPosition pos = mMapPosition;
|
||||
|
||||
tile.lastDraw = mDrawSerial;
|
||||
|
||||
setMatrix(mvp, tile, div, true);
|
||||
|
||||
if (tile.holder != null)
|
||||
tile = tile.holder;
|
||||
|
||||
GLES20.glPolygonOffset(0, mDrawCount++);
|
||||
|
||||
GLES20.glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id);
|
||||
|
||||
LineLayer ll = tile.lineLayers;
|
||||
PolygonLayer pl = tile.polygonLayers;
|
||||
|
||||
boolean clipped = false;
|
||||
int simpleShader = mRotate ? 0 : 1;
|
||||
|
||||
for (; pl != null || ll != null;) {
|
||||
int lnext = Integer.MAX_VALUE;
|
||||
int pnext = Integer.MAX_VALUE;
|
||||
|
||||
if (ll != null)
|
||||
lnext = ll.layer;
|
||||
|
||||
if (pl != null)
|
||||
pnext = pl.layer;
|
||||
|
||||
if (pl != null && pnext < lnext) {
|
||||
GLES20.glDisable(GL_BLEND);
|
||||
pl = PolygonRenderer.drawPolygons(pos, pl, lnext, mvp, !clipped);
|
||||
clipped = true;
|
||||
} else {
|
||||
// FIXME
|
||||
if (!clipped) {
|
||||
PolygonRenderer.drawPolygons(pos, null, 0, mvp, true);
|
||||
clipped = true;
|
||||
}
|
||||
GLES20.glEnable(GL_BLEND);
|
||||
ll = LineRenderer.drawLines(pos, ll, pnext, mvp, div,
|
||||
simpleShader, tile.lineOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO should check tile.proxies here
|
||||
private static boolean drawProxyChild(MapTile tile) {
|
||||
int drawn = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (tile.rel.child[i] == null)
|
||||
continue;
|
||||
|
||||
MapTile c = tile.rel.child[i].tile;
|
||||
if (c == null)
|
||||
continue;
|
||||
|
||||
// if (!isVisible(c)) {
|
||||
// drawn++;
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if (c.isReady) {
|
||||
drawTile(c);
|
||||
drawn++;
|
||||
}
|
||||
}
|
||||
return drawn == 4;
|
||||
}
|
||||
|
||||
private static void drawProxyTile(MapTile tile) {
|
||||
int diff = mMapPosition.zoomLevel - tile.zoomLevel;
|
||||
|
||||
boolean drawn = false;
|
||||
if (mMapPosition.scale > 1.5f || diff < 0) {
|
||||
// prefer drawing children
|
||||
if (!drawProxyChild(tile)) {
|
||||
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
|
||||
MapTile t = tile.rel.parent.tile;
|
||||
if (t.isReady) {
|
||||
drawTile(t);
|
||||
drawn = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!drawn && (tile.proxies & MapTile.PROXY_GRAMPA) != 0) {
|
||||
MapTile t = tile.rel.parent.parent.tile;
|
||||
if (t.isReady)
|
||||
drawTile(t);
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// prefer drawing parent
|
||||
MapTile t = tile.rel.parent.tile;
|
||||
|
||||
if (t != null && t.isReady) {
|
||||
drawTile(t);
|
||||
|
||||
} else if (!drawProxyChild(tile)) {
|
||||
|
||||
if ((tile.proxies & MapTile.PROXY_GRAMPA) != 0) {
|
||||
t = tile.rel.parent.parent.tile;
|
||||
if (t.isReady)
|
||||
drawTile(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
|
||||
Log.d(TAG, "SurfaceChanged:" + width + " " + height);
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
boolean changed = true;
|
||||
if (mWidth == width || mHeight == height)
|
||||
changed = false;
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
|
||||
// use this to scale only the view to see better which tiles are
|
||||
// rendered
|
||||
float s = MapViewPosition.VIEW_SCALE;
|
||||
Matrix.frustumM(mProjMatrix, 0, -s * width, s * width,
|
||||
s * height, -s * height, 1, 2);
|
||||
|
||||
Matrix.translateM(mProjMatrix, 0, 0, 0, -MapViewPosition.VIEW_DISTANCE);
|
||||
|
||||
// set to zero: we modify the z value with polygon-offset for clipping
|
||||
mProjMatrix[10] = 0;
|
||||
mProjMatrix[14] = 0;
|
||||
|
||||
GLES20.glViewport(0, 0, width, height);
|
||||
|
||||
if (!changed && !mNewSurface) {
|
||||
mMapView.redrawMap();
|
||||
return;
|
||||
}
|
||||
mNewSurface = false;
|
||||
|
||||
mBufferMemoryUsage = 0;
|
||||
|
||||
int numTiles = (mWidth / (Tile.TILE_SIZE / 2) + 2)
|
||||
* (mHeight / (Tile.TILE_SIZE / 2) + 2);
|
||||
|
||||
// Set up vertex buffer objects
|
||||
int numVBO = (CACHE_TILES + (numTiles * 2));
|
||||
int[] mVboIds = new int[numVBO];
|
||||
GLES20.glGenBuffers(numVBO, mVboIds, 0);
|
||||
GlUtils.checkGlError("glGenBuffers");
|
||||
|
||||
mVBOs = new ArrayList<VertexBufferObject>(numVBO);
|
||||
|
||||
for (int i = 1; i < numVBO; i++)
|
||||
mVBOs.add(new VertexBufferObject(mVboIds[i]));
|
||||
|
||||
// Set up textures
|
||||
TextRenderer.setup(numTiles);
|
||||
|
||||
if (mClearColor != null)
|
||||
mUpdateColor = true;
|
||||
|
||||
// FIXME this should be synchronized
|
||||
mMapView.redrawMap();
|
||||
}
|
||||
|
||||
// FIXME this is a bit too spaghetti
|
||||
void clearTiles(int numTiles) {
|
||||
mDrawTiles = new TilesData(numTiles);
|
||||
mNextTiles = new TilesData(numTiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
|
||||
String ext = GLES20.glGetString(GLES20.GL_EXTENSIONS);
|
||||
Log.d(TAG, "Extensions: " + ext);
|
||||
|
||||
LineRenderer.init();
|
||||
PolygonRenderer.init();
|
||||
TextRenderer.init();
|
||||
|
||||
mNewSurface = true;
|
||||
// mUpdateColor = true;
|
||||
|
||||
// glEnable(GL_SCISSOR_TEST);
|
||||
// glScissor(0, 0, mWidth, mHeight);
|
||||
GLES20.glClearStencil(0);
|
||||
GLES20.glDisable(GLES20.GL_CULL_FACE);
|
||||
GLES20.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
}
|
||||
|
||||
private boolean mNewSurface;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// private static boolean isVisible(MapTile tile) {
|
||||
// float dx, dy, scale, div = 1;
|
||||
// MapPosition mapPosition = mMapPosition;
|
||||
// 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;
|
||||
// ssize += Tile.TILE_SIZE;
|
||||
// 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;
|
||||
// }
|
||||
@@ -1,507 +0,0 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.renderer;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.theme.renderinstruction.Line;
|
||||
|
||||
import android.graphics.Paint.Cap;
|
||||
import android.util.FloatMath;
|
||||
|
||||
class LineLayer {
|
||||
|
||||
private static final float COORD_SCALE = GLRenderer.COORD_MULTIPLIER;
|
||||
// scale factor mapping extrusion vector to short values
|
||||
private static final float DIR_SCALE = 2048;
|
||||
// mask for packing last two bits of extrusion vector with texture
|
||||
// coordinates
|
||||
private static final int DIR_MASK = 0xFFFFFFFC;
|
||||
|
||||
// next layer
|
||||
LineLayer next;
|
||||
|
||||
// lines referenced by this outline layer
|
||||
LineLayer outlines;
|
||||
|
||||
Line line;
|
||||
float width;
|
||||
boolean isOutline;
|
||||
int layer;
|
||||
|
||||
VertexPoolItem pool;
|
||||
protected VertexPoolItem curItem;
|
||||
|
||||
// number of vertices this layer holds
|
||||
int verticesCnt;
|
||||
// vertices offset of this layer in VBO
|
||||
int offset;
|
||||
|
||||
LineLayer(int layer, Line line, float width, boolean outline) {
|
||||
this.layer = layer;
|
||||
this.width = width;
|
||||
this.line = line;
|
||||
this.isOutline = outline;
|
||||
}
|
||||
|
||||
void addOutline(LineLayer link) {
|
||||
for (LineLayer l = outlines; l != null; l = l.outlines)
|
||||
if (link == l)
|
||||
return;
|
||||
|
||||
link.outlines = outlines;
|
||||
outlines = link;
|
||||
}
|
||||
|
||||
/*
|
||||
* line extrusion is based on code from GLMap
|
||||
* (https://github.com/olofsj/GLMap/) by olofsj
|
||||
*/
|
||||
|
||||
void addLine(float[] points, short[] index, boolean closed) {
|
||||
float x, y, nextX, nextY, prevX, prevY;
|
||||
float a, ux, uy, vx, vy, wx, wy;
|
||||
|
||||
int tmax = Tile.TILE_SIZE + 10;
|
||||
int tmin = -10;
|
||||
|
||||
boolean rounded = false;
|
||||
boolean squared = false;
|
||||
|
||||
if (line.cap == Cap.ROUND)
|
||||
rounded = true;
|
||||
else if (line.cap == Cap.SQUARE)
|
||||
squared = true;
|
||||
|
||||
if (pool == null) {
|
||||
pool = curItem = VertexPool.get();
|
||||
}
|
||||
|
||||
VertexPoolItem si = curItem;
|
||||
short v[] = si.vertices;
|
||||
int opos = si.used;
|
||||
|
||||
for (int i = 0, pos = 0, n = index.length; i < n; i++) {
|
||||
|
||||
int length = index[i];
|
||||
if (length < 0)
|
||||
break;
|
||||
|
||||
// save some vertices
|
||||
if (rounded && i > 200)
|
||||
rounded = false;
|
||||
|
||||
// need at least two points
|
||||
if (length < 4) {
|
||||
pos += length;
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
closed = false;
|
||||
|
||||
// amount of vertices used
|
||||
// + 2 for drawing triangle-strip
|
||||
// + 4 for round caps
|
||||
// + 2 for closing polygons
|
||||
verticesCnt += length + (rounded ? 6 : 2) + (closed ? 2 : 0);
|
||||
|
||||
int ipos = pos;
|
||||
|
||||
x = points[ipos++];
|
||||
y = points[ipos++];
|
||||
|
||||
nextX = points[ipos++];
|
||||
nextY = points[ipos++];
|
||||
|
||||
// Calculate triangle corners for the given width
|
||||
vx = nextX - x;
|
||||
vy = nextY - y;
|
||||
|
||||
a = FloatMath.sqrt(vx * vx + vy * vy);
|
||||
|
||||
vx = (vx / a);
|
||||
vy = (vy / a);
|
||||
|
||||
ux = -vy;
|
||||
uy = vx;
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
short ox, oy, dx, dy;
|
||||
int ddx, ddy;
|
||||
|
||||
ox = (short) (x * COORD_SCALE);
|
||||
oy = (short) (y * COORD_SCALE);
|
||||
|
||||
boolean outside = (x < tmin || x > tmax || y < tmin || y > tmax);
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
if (rounded && !outside) {
|
||||
// add first vertex twice
|
||||
ddx = (int) ((ux - vx) * DIR_SCALE);
|
||||
ddy = (int) ((uy - vy) * DIR_SCALE);
|
||||
// last two bit encode texture coord (-1)
|
||||
dx = (short) (0 | ddx & DIR_MASK);
|
||||
dy = (short) (2 | ddy & DIR_MASK);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
ddx = (int) (-(ux + vx) * DIR_SCALE);
|
||||
ddy = (int) (-(uy + vy) * DIR_SCALE);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (2 | ddx & DIR_MASK);
|
||||
v[opos++] = (short) (2 | ddy & DIR_MASK);
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
// Start of line
|
||||
ddx = (int) (ux * DIR_SCALE);
|
||||
ddy = (int) (uy * DIR_SCALE);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (0 | ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (2 | -ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | -ddy & DIR_MASK);
|
||||
|
||||
} else {
|
||||
// outside means line is probably clipped
|
||||
// TODO should align ending with tile boundary
|
||||
// for now, just extend the line a little
|
||||
|
||||
if (squared) {
|
||||
vx = 0;
|
||||
vy = 0;
|
||||
} else if (!outside) {
|
||||
vx *= 0.5;
|
||||
vy *= 0.5;
|
||||
}
|
||||
|
||||
if (rounded)
|
||||
verticesCnt -= 2;
|
||||
|
||||
// add first vertex twice
|
||||
ddx = (int) ((ux - vx) * DIR_SCALE);
|
||||
ddy = (int) ((uy - vy) * DIR_SCALE);
|
||||
dx = (short) (0 | ddx & DIR_MASK);
|
||||
dy = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
ddx = (int) (-(ux + vx) * DIR_SCALE);
|
||||
ddy = (int) (-(uy + vy) * DIR_SCALE);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (2 | ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
}
|
||||
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
x = nextX;
|
||||
y = nextY;
|
||||
|
||||
for (;;) {
|
||||
if (ipos < pos + length) {
|
||||
nextX = points[ipos++];
|
||||
nextY = points[ipos++];
|
||||
} else if (closed && ipos < pos + length + 2) {
|
||||
// add startpoint == endpoint
|
||||
nextX = points[pos];
|
||||
nextY = points[pos + 1];
|
||||
ipos += 2;
|
||||
} else
|
||||
break;
|
||||
|
||||
// Unit vector pointing back to previous node
|
||||
vx = prevX - x;
|
||||
vy = prevY - y;
|
||||
a = FloatMath.sqrt(vx * vx + vy * vy);
|
||||
vx = (vx / a);
|
||||
vy = (vy / a);
|
||||
|
||||
// Unit vector pointing forward to next node
|
||||
wx = nextX - x;
|
||||
wy = nextY - y;
|
||||
a = FloatMath.sqrt(wx * wx + wy * wy);
|
||||
wx = (wx / a);
|
||||
wy = (wy / a);
|
||||
|
||||
// Sum of these two vectors points
|
||||
ux = vx + wx;
|
||||
uy = vy + wy;
|
||||
|
||||
a = -wy * ux + wx * uy;
|
||||
|
||||
// boolean split = false;
|
||||
if (a < 0.01f && a > -0.01f) {
|
||||
// Almost straight
|
||||
ux = -wy;
|
||||
uy = wx;
|
||||
} else {
|
||||
ux = (ux / a);
|
||||
uy = (uy / a);
|
||||
|
||||
// hack to avoid miter going to infinity
|
||||
if (ux > 2.0f || ux < -2.0f || uy > 2.0f || uy < -2.0f) {
|
||||
ux = -wy;
|
||||
uy = wx;
|
||||
}
|
||||
}
|
||||
|
||||
ox = (short) (x * COORD_SCALE);
|
||||
oy = (short) (y * COORD_SCALE);
|
||||
|
||||
ddx = (int) (ux * DIR_SCALE);
|
||||
ddy = (int) (uy * DIR_SCALE);
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (0 | ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (2 | -ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | -ddy & DIR_MASK);
|
||||
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
x = nextX;
|
||||
y = nextY;
|
||||
}
|
||||
|
||||
vx = prevX - x;
|
||||
vy = prevY - y;
|
||||
|
||||
a = FloatMath.sqrt(vx * vx + vy * vy);
|
||||
|
||||
vx = (vx / a);
|
||||
vy = (vy / a);
|
||||
|
||||
ux = vy;
|
||||
uy = -vx;
|
||||
|
||||
outside = (x < tmin || x > tmax || y < tmin || y > tmax);
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si.next = VertexPool.get();
|
||||
si = si.next;
|
||||
opos = 0;
|
||||
v = si.vertices;
|
||||
}
|
||||
|
||||
ox = (short) (x * COORD_SCALE);
|
||||
oy = (short) (y * COORD_SCALE);
|
||||
|
||||
if (rounded && !outside) {
|
||||
ddx = (int) (ux * DIR_SCALE);
|
||||
ddy = (int) (uy * DIR_SCALE);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (0 | ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (2 | -ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | -ddy & DIR_MASK);
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
// For rounded line edges
|
||||
ddx = (int) ((ux - vx) * DIR_SCALE);
|
||||
ddy = (int) ((uy - vy) * DIR_SCALE);
|
||||
dx = (short) (0 | ddx & DIR_MASK);
|
||||
dy = (short) (0 | ddy & DIR_MASK);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
// add last vertex twice
|
||||
ddx = (int) (-(ux + vx) * DIR_SCALE);
|
||||
ddy = (int) (-(uy + vy) * DIR_SCALE);
|
||||
dx = (short) (2 | ddx & DIR_MASK);
|
||||
dy = (short) (0 | ddy & DIR_MASK);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
} else {
|
||||
if (squared) {
|
||||
vx = 0;
|
||||
vy = 0;
|
||||
} else if (!outside) {
|
||||
vx *= 0.5;
|
||||
vy *= 0.5;
|
||||
}
|
||||
|
||||
if (rounded)
|
||||
verticesCnt -= 2;
|
||||
|
||||
ddx = (int) ((ux - vx) * DIR_SCALE);
|
||||
ddy = (int) ((uy - vy) * DIR_SCALE);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (0 | ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
// add last vertex twice
|
||||
ddx = (int) (-(ux + vx) * DIR_SCALE);
|
||||
ddy = (int) (-(uy + vy) * DIR_SCALE);
|
||||
dx = (short) (2 | ddx & DIR_MASK);
|
||||
dy = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
}
|
||||
pos += length;
|
||||
}
|
||||
|
||||
si.used = opos;
|
||||
curItem = si;
|
||||
}
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.renderer;
|
||||
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import org.oscim.theme.renderinstruction.Line;
|
||||
import org.oscim.utils.GlUtils;
|
||||
import org.oscim.view.MapPosition;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
class LineRenderer {
|
||||
private final static String TAG = "LineRenderer";
|
||||
|
||||
private static int NUM_VERTEX_SHORTS = 4;
|
||||
|
||||
private static final int LINE_VERTICES_DATA_POS_OFFSET = 0;
|
||||
private static final int LINE_VERTICES_DATA_TEX_OFFSET = 4;
|
||||
|
||||
// shader handles
|
||||
private static int[] lineProgram = new int[2];
|
||||
private static int[] hLineVertexPosition = new int[2];
|
||||
private static int[] hLineTexturePosition = new int[2];
|
||||
private static int[] hLineColor = new int[2];
|
||||
private static int[] hLineMatrix = new int[2];
|
||||
private static int[] hLineScale = new int[2];
|
||||
private static int[] hLineWidth = new int[2];
|
||||
|
||||
static boolean init() {
|
||||
lineProgram[0] = GlUtils.createProgram(Shaders.lineVertexShader,
|
||||
Shaders.lineFragmentShader);
|
||||
if (lineProgram[0] == 0) {
|
||||
Log.e(TAG, "Could not create line program.");
|
||||
return false;
|
||||
}
|
||||
|
||||
hLineMatrix[0] = GLES20.glGetUniformLocation(lineProgram[0], "u_mvp");
|
||||
hLineScale[0] = GLES20.glGetUniformLocation(lineProgram[0], "u_wscale");
|
||||
hLineWidth[0] = GLES20.glGetUniformLocation(lineProgram[0], "u_width");
|
||||
hLineColor[0] = GLES20.glGetUniformLocation(lineProgram[0], "u_color");
|
||||
|
||||
hLineVertexPosition[0] = GLES20.glGetAttribLocation(lineProgram[0], "a_position");
|
||||
hLineTexturePosition[0] = GLES20.glGetAttribLocation(lineProgram[0], "a_st");
|
||||
|
||||
lineProgram[1] = GlUtils.createProgram(Shaders.lineVertexShader,
|
||||
Shaders.lineSimpleFragmentShader);
|
||||
if (lineProgram[1] == 0) {
|
||||
Log.e(TAG, "Could not create simple line program.");
|
||||
return false;
|
||||
}
|
||||
|
||||
hLineMatrix[1] = GLES20.glGetUniformLocation(lineProgram[1], "u_mvp");
|
||||
hLineScale[1] = GLES20.glGetUniformLocation(lineProgram[1], "u_wscale");
|
||||
hLineWidth[1] = GLES20.glGetUniformLocation(lineProgram[1], "u_width");
|
||||
hLineColor[1] = GLES20.glGetUniformLocation(lineProgram[1], "u_color");
|
||||
|
||||
hLineVertexPosition[1] = GLES20.glGetAttribLocation(lineProgram[1], "a_position");
|
||||
hLineTexturePosition[1] = GLES20.glGetAttribLocation(lineProgram[1], "a_st");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static LineLayer drawLines(MapPosition pos, LineLayer layer, int next,
|
||||
float[] matrix, float div, int mode, int bufferOffset) {
|
||||
|
||||
int zoom = pos.zoomLevel;
|
||||
float scale = pos.scale;
|
||||
|
||||
if (layer == null)
|
||||
return null;
|
||||
|
||||
GLES20.glUseProgram(lineProgram[mode]);
|
||||
|
||||
GLES20.glEnableVertexAttribArray(hLineVertexPosition[mode]);
|
||||
GLES20.glEnableVertexAttribArray(hLineTexturePosition[mode]);
|
||||
|
||||
GLES20.glVertexAttribPointer(hLineVertexPosition[mode], 2, GLES20.GL_SHORT,
|
||||
false, 8, bufferOffset + LINE_VERTICES_DATA_POS_OFFSET);
|
||||
|
||||
GLES20.glVertexAttribPointer(hLineTexturePosition[mode], 2, GLES20.GL_SHORT,
|
||||
false, 8, bufferOffset + LINE_VERTICES_DATA_TEX_OFFSET);
|
||||
|
||||
GLES20.glUniformMatrix4fv(hLineMatrix[mode], 1, false, matrix, 0);
|
||||
|
||||
// scale factor to map one pixel on tile to one pixel on screen:
|
||||
// only works with orthographic projection
|
||||
float s = scale / div;
|
||||
float pixel = 2.0f / s;
|
||||
|
||||
if (mode == 0)
|
||||
pixel = 0;
|
||||
|
||||
GLES20.glUniform1f(hLineScale[mode], pixel);
|
||||
|
||||
// line scale factor (for non fixed lines)
|
||||
float lineScale = FloatMath.sqrt(s);
|
||||
float blurScale = pixel;
|
||||
boolean blur = false;
|
||||
// dont increase scale when max is reached
|
||||
boolean strokeMaxZoom = zoom > TileGenerator.STROKE_MAX_ZOOM_LEVEL;
|
||||
float width = 1;
|
||||
|
||||
LineLayer l = layer;
|
||||
for (; l != null && l.layer < next; l = l.next) {
|
||||
|
||||
Line line = l.line;
|
||||
if (line.fade != -1 && line.fade > zoom)
|
||||
continue;
|
||||
|
||||
float alpha = 1.0f;
|
||||
|
||||
if (line.fade >= zoom)
|
||||
alpha = (scale > 1.2f ? scale : 1.2f) - alpha;
|
||||
|
||||
GlUtils.setColor(hLineColor[mode], line.color, alpha);
|
||||
|
||||
if (blur && line.blur == 0) {
|
||||
GLES20.glUniform1f(hLineScale[mode], pixel);
|
||||
blur = false;
|
||||
}
|
||||
|
||||
if (l.isOutline) {
|
||||
for (LineLayer o = l.outlines; o != null; o = o.outlines) {
|
||||
|
||||
if (o.line.fixed || strokeMaxZoom) {
|
||||
width = (l.width + o.width) / s;
|
||||
} else {
|
||||
width = l.width / s + o.width / lineScale;
|
||||
}
|
||||
|
||||
GLES20.glUniform1f(hLineWidth[mode], width);
|
||||
|
||||
if (line.blur != 0) {
|
||||
blurScale = (l.width + o.width) / s - (line.blur / s);
|
||||
GLES20.glUniform1f(hLineScale[mode], blurScale);
|
||||
blur = true;
|
||||
}
|
||||
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, o.offset, o.verticesCnt);
|
||||
}
|
||||
} else {
|
||||
|
||||
if (line.fixed || strokeMaxZoom) {
|
||||
// invert scaling of extrusion vectors so that line width
|
||||
// stays the same.
|
||||
width = l.width / s;
|
||||
} else {
|
||||
width = l.width / lineScale;
|
||||
}
|
||||
|
||||
GLES20.glUniform1f(hLineWidth[mode], width);
|
||||
|
||||
if (line.blur != 0) {
|
||||
blurScale = (l.width / lineScale) * line.blur;
|
||||
GLES20.glUniform1f(hLineScale[mode], blurScale);
|
||||
blur = true;
|
||||
}
|
||||
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, l.offset, l.verticesCnt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GLES20.glDisableVertexAttribArray(hLineVertexPosition[mode]);
|
||||
GLES20.glDisableVertexAttribArray(hLineTexturePosition[mode]);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static int sizeOf(LineLayer layers) {
|
||||
int size = 0;
|
||||
for (LineLayer l = layers; l != null; l = l.next)
|
||||
size += l.verticesCnt;
|
||||
|
||||
size *= NUM_VERTEX_SHORTS;
|
||||
return size;
|
||||
}
|
||||
|
||||
static void compileLayerData(LineLayer layers, ShortBuffer sbuf) {
|
||||
int pos = 0;
|
||||
VertexPoolItem last = null, items = null;
|
||||
|
||||
for (LineLayer l = layers; l != null; l = l.next) {
|
||||
if (l.isOutline)
|
||||
continue;
|
||||
|
||||
for (VertexPoolItem item = l.pool; item != null; item = item.next) {
|
||||
|
||||
if (item.next == null) {
|
||||
sbuf.put(item.vertices, 0, item.used);
|
||||
} else {
|
||||
// item.used = VertexPoolItem.SIZE;
|
||||
sbuf.put(item.vertices);
|
||||
}
|
||||
|
||||
last = item;
|
||||
}
|
||||
|
||||
l.offset = pos;
|
||||
pos += l.verticesCnt;
|
||||
|
||||
if (last != null) {
|
||||
last.next = items;
|
||||
items = l.pool;
|
||||
}
|
||||
|
||||
l.pool = null;
|
||||
l.curItem = null;
|
||||
}
|
||||
|
||||
VertexPool.add(items);
|
||||
}
|
||||
|
||||
// @SuppressLint("UseValueOf")
|
||||
// private static final Boolean lock = new Boolean(true);
|
||||
// private static final int POOL_LIMIT = 1500;
|
||||
//
|
||||
// static private LineLayer pool = null;
|
||||
// static private int count = 0;
|
||||
// static private int countAll = 0;
|
||||
//
|
||||
// static void finish() {
|
||||
// synchronized (lock) {
|
||||
// count = 0;
|
||||
// countAll = 0;
|
||||
// pool = null;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// static LineLayer get(int layer, Line line, float width, boolean outline)
|
||||
// {
|
||||
// synchronized (lock) {
|
||||
//
|
||||
// if (count == 0 && pool == null) {
|
||||
// countAll++;
|
||||
// return new LineLayer(layer, line, width, outline);
|
||||
// }
|
||||
// if (count > 0) {
|
||||
// count--;
|
||||
// } else {
|
||||
// int c = 0;
|
||||
// LineLayer tmp = pool;
|
||||
//
|
||||
// while (tmp != null) {
|
||||
// c++;
|
||||
// tmp = tmp.next;
|
||||
// }
|
||||
//
|
||||
// Log.d("LineLayersl", "eek wrong count: " + c + " left");
|
||||
// }
|
||||
//
|
||||
// LineLayer it = pool;
|
||||
// pool = pool.next;
|
||||
// it.next = null;
|
||||
// it.layer = layer;
|
||||
// it.line = line;
|
||||
// it.isOutline = outline;
|
||||
// it.width = width;
|
||||
// return it;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// static void add(LineLayer layers) {
|
||||
// if (layers == null)
|
||||
// return;
|
||||
//
|
||||
// synchronized (lock) {
|
||||
//
|
||||
// // limit pool items
|
||||
// if (countAll < POOL_LIMIT) {
|
||||
// LineLayer last = layers;
|
||||
//
|
||||
// while (true) {
|
||||
// count++;
|
||||
//
|
||||
// if (last.next == null)
|
||||
// break;
|
||||
//
|
||||
// last = last.next;
|
||||
// }
|
||||
//
|
||||
// last.next = pool;
|
||||
// pool = layers;
|
||||
//
|
||||
// } else {
|
||||
// int cleared = 0;
|
||||
// LineLayer prev, tmp = layers;
|
||||
// while (tmp != null) {
|
||||
// prev = tmp;
|
||||
// tmp = tmp.next;
|
||||
//
|
||||
// countAll--;
|
||||
// cleared++;
|
||||
//
|
||||
// prev.next = null;
|
||||
//
|
||||
// }
|
||||
// Log.d("LineLayers", "sum: " + countAll + " free: " + count + " freed "
|
||||
// + cleared);
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
//
|
||||
static void clear(LineLayer layer) {
|
||||
for (LineLayer l = layer; l != null; l = l.next) {
|
||||
if (l.pool != null) {
|
||||
VertexPool.add(l.pool);
|
||||
l.pool = null;
|
||||
l.curItem = null;
|
||||
}
|
||||
}
|
||||
// LineLayers.add(layer);
|
||||
}
|
||||
}
|
||||
@@ -1,651 +0,0 @@
|
||||
/*
|
||||
* 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.renderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.theme.RenderTheme;
|
||||
import org.oscim.utils.GlConfigChooser;
|
||||
import org.oscim.view.MapPosition;
|
||||
import org.oscim.view.MapView;
|
||||
import org.oscim.view.MapViewPosition;
|
||||
import org.oscim.view.generator.JobTile;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
// FIXME to many 'Renderer', this one needs a better name.. TileLoader?
|
||||
public class MapRenderer extends GLSurfaceView {
|
||||
private final static String TAG = "MapRenderer";
|
||||
private GLRenderer mRenderer;
|
||||
|
||||
private static final int MAX_TILES_IN_QUEUE = 40;
|
||||
private static final int CACHE_THRESHOLD = 50;
|
||||
|
||||
private static MapView mMapView;
|
||||
|
||||
private static final MapPosition mMapPosition = new MapPosition();
|
||||
private final MapViewPosition mMapViewPosition;
|
||||
|
||||
// new jobs for the MapWorkers
|
||||
private static ArrayList<JobTile> mJobList;
|
||||
|
||||
// all tiles currently referenced
|
||||
private static ArrayList<MapTile> mTiles;
|
||||
|
||||
// tiles that have new data to upload, see passTile()
|
||||
private static ArrayList<MapTile> mTilesLoaded;
|
||||
|
||||
// TODO current boundary tiles, values used to check if position has
|
||||
// changed for updating current tile list
|
||||
|
||||
private static boolean mInitial;
|
||||
|
||||
// private static MapPosition mCurPosition, mDrawPosition;
|
||||
private static int mWidth = 0, mHeight = 0;
|
||||
|
||||
// maps zoom-level to available zoom-levels in MapDatabase
|
||||
// e.g. 16->16, 15->16, 14->13, 13->13, 12->13,....
|
||||
// private static int[] mZoomLevels;
|
||||
|
||||
private static float[] mTileCoords = new float[8];
|
||||
private static int[] mBoundaryTiles = new int[8];
|
||||
|
||||
// used for passing tiles to be rendered from TileLoader(Main-Thread) to
|
||||
// GLThread
|
||||
static final class TilesData {
|
||||
int cnt = 0;
|
||||
final MapTile[] tiles;
|
||||
|
||||
TilesData(int numTiles) {
|
||||
tiles = new MapTile[numTiles];
|
||||
}
|
||||
}
|
||||
|
||||
/* package */static TilesData mCurrentTiles;
|
||||
|
||||
private static ScanBox mScanBox = new ScanBox() {
|
||||
|
||||
@Override
|
||||
void setVisible(int y, int x1, int x2) {
|
||||
MapTile[] tiles = mCurrentTiles.tiles;
|
||||
int cnt = mCurrentTiles.cnt;
|
||||
int max = mCurrentTiles.tiles.length;
|
||||
int xmax = 1 << mZoom;
|
||||
|
||||
for (int x = x1; x < x2; x++) {
|
||||
// MapTile holder = null;
|
||||
MapTile tile = null;
|
||||
|
||||
// boolean found = false;
|
||||
if (cnt == max) {
|
||||
Log.d(TAG, "reached max currentTiles " + max);
|
||||
break;
|
||||
}
|
||||
int xx = x;
|
||||
|
||||
if (x < 0 || x >= xmax) {
|
||||
// flip-around date line
|
||||
if (x < 0)
|
||||
xx = xmax + x;
|
||||
else
|
||||
xx = x - xmax;
|
||||
|
||||
if (xx < 0 || xx >= xmax) {
|
||||
// Log.d(TAG, "tile out of bounds " + y + " " + xx);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < cnt; i++)
|
||||
if (tiles[i].tileX == xx && tiles[i].tileY == y) {
|
||||
tile = tiles[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if (tile == null) {
|
||||
tile = addTile(xx, y, mZoom, 0);
|
||||
tiles[cnt++] = tile;
|
||||
}
|
||||
}
|
||||
mCurrentTiles.cnt = cnt;
|
||||
}
|
||||
};
|
||||
|
||||
public MapRenderer(Context context, MapView mapView) {
|
||||
super(context);
|
||||
|
||||
mMapView = mapView;
|
||||
mMapViewPosition = mapView.getMapViewPosition();
|
||||
|
||||
Log.d(TAG, "init GLSurfaceLayer");
|
||||
setEGLConfigChooser(new GlConfigChooser());
|
||||
setEGLContextClientVersion(2);
|
||||
|
||||
// setDebugFlags(DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS);
|
||||
mRenderer = new GLRenderer(mMapView);
|
||||
setRenderer(mRenderer);
|
||||
|
||||
// if (!debugFrameTime)
|
||||
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
|
||||
|
||||
mJobList = new ArrayList<JobTile>();
|
||||
mTiles = new ArrayList<MapTile>();
|
||||
mTilesLoaded = new ArrayList<MapTile>(30);
|
||||
|
||||
VertexPool.init();
|
||||
QuadTree.init();
|
||||
|
||||
mInitial = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update list of visible tiles and passes them to MapRenderer, when not
|
||||
* available tiles are created and added to JobQueue (mapView.addJobs) for
|
||||
* loading by TileGenerator class
|
||||
*
|
||||
* @param clear
|
||||
* whether to clear and reload all tiles
|
||||
*/
|
||||
public void updateMap(boolean clear) {
|
||||
boolean changedPos = false;
|
||||
|
||||
if (mMapView == null)
|
||||
return;
|
||||
|
||||
if (clear || mInitial) {
|
||||
// make sure onDrawFrame is not running
|
||||
GLRenderer.drawlock.lock();
|
||||
// remove all tiles references
|
||||
Log.d(TAG, "CLEAR");
|
||||
for (MapTile t : mTiles)
|
||||
clearTile(t);
|
||||
|
||||
mTiles.clear();
|
||||
mTilesLoaded.clear();
|
||||
QuadTree.init();
|
||||
|
||||
// set up TileData arrays that are passed to gl-thread
|
||||
int num = mWidth;
|
||||
if (mWidth < mHeight)
|
||||
num = mHeight;
|
||||
|
||||
int size = Tile.TILE_SIZE >> 1;
|
||||
|
||||
int numTiles = (num * num) / (size * size) * 4;
|
||||
|
||||
mRenderer.clearTiles(numTiles);
|
||||
mCurrentTiles = new TilesData(numTiles);
|
||||
|
||||
// MapInfo mapInfo = mMapView.getMapDatabase().getMapInfo();
|
||||
// if (mapInfo != null)
|
||||
// mZoomLevels = mapInfo.zoomLevel;
|
||||
GLRenderer.drawlock.unlock();
|
||||
|
||||
changedPos = true;
|
||||
mInitial = false;
|
||||
}
|
||||
|
||||
MapPosition mapPosition = mMapPosition;
|
||||
mMapViewPosition.getMapPosition(mapPosition, mTileCoords);
|
||||
|
||||
float s = Tile.TILE_SIZE;
|
||||
// load some additional tiles more than currently visible
|
||||
float scale = mapPosition.scale * 0.75f;
|
||||
float px = (float) mapPosition.x;
|
||||
float py = (float) mapPosition.y;
|
||||
float[] coords = mTileCoords;
|
||||
int zdir = 0;
|
||||
|
||||
for (int i = 0; i < 8; i += 2) {
|
||||
coords[i + 0] = (px + coords[i + 0] / scale) / s;
|
||||
coords[i + 1] = (py + coords[i + 1] / scale) / s;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (mBoundaryTiles[i] != (int) coords[i]) {
|
||||
changedPos = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
mBoundaryTiles[i] = (int) coords[i];
|
||||
|
||||
// TODO all following should probably be done in an idler instead
|
||||
// to drain queued events. need to check how android handles things.
|
||||
|
||||
if (changedPos) {
|
||||
updateVisibleList(mapPosition, zdir);
|
||||
|
||||
if (!MapView.debugFrameTime)
|
||||
requestRender();
|
||||
|
||||
int remove = mTiles.size() - GLRenderer.CACHE_TILES;
|
||||
if (remove > CACHE_THRESHOLD)
|
||||
limitCache(mapPosition, remove);
|
||||
|
||||
limitLoadQueue();
|
||||
} else {
|
||||
if (!MapView.debugFrameTime)
|
||||
requestRender();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set mCurrentTiles for the visible tiles and pass it to GLRenderer, add
|
||||
* jobs for not yet loaded tiles
|
||||
*
|
||||
* @param mapPosition
|
||||
* the current MapPosition
|
||||
* @param zdir
|
||||
* zoom direction
|
||||
*/
|
||||
private static void updateVisibleList(MapPosition mapPosition, int zdir) {
|
||||
|
||||
mJobList.clear();
|
||||
// set non processed tiles to isLoading == false
|
||||
mMapView.addJobs(null);
|
||||
mCurrentTiles.cnt = 0;
|
||||
mScanBox.scan(mTileCoords, mapPosition.zoomLevel);
|
||||
// Log.d(TAG, "visible: " + mCurrentTiles.cnt + "/" +
|
||||
// mCurrentTiles.tiles.length);
|
||||
GLRenderer.updateTiles(mCurrentTiles);
|
||||
|
||||
// note: this sets isLoading == true for all job tiles
|
||||
if (mJobList.size() > 0) {
|
||||
updateTileDistances(mJobList, mapPosition);
|
||||
Collections.sort(mJobList);
|
||||
mMapView.addJobs(mJobList);
|
||||
}
|
||||
}
|
||||
|
||||
/* package */
|
||||
static MapTile addTile(int x, int y, byte zoomLevel, int zdir) {
|
||||
MapTile tile;
|
||||
|
||||
tile = QuadTree.getTile(x, y, zoomLevel);
|
||||
|
||||
if (tile == null) {
|
||||
tile = new MapTile(x, y, zoomLevel);
|
||||
|
||||
QuadTree.add(tile);
|
||||
mTiles.add(tile);
|
||||
}
|
||||
|
||||
// if (!fetchProxy && !tile.isActive()) {
|
||||
if (!tile.isActive()) {
|
||||
mJobList.add(tile);
|
||||
}
|
||||
|
||||
// mCurrentTiles.tiles[tiles++] = tile;
|
||||
|
||||
// if (fetchChildren) {
|
||||
// byte z = (byte) (zoomLevel + 1);
|
||||
// for (int i = 0; i < 4; i++) {
|
||||
// int cx = (xx << 1) + (i % 2);
|
||||
// int cy = (yy << 1) + (i >> 1);
|
||||
//
|
||||
// MapTile c = QuadTree.getTile(cx, cy, z);
|
||||
//
|
||||
// if (c == null) {
|
||||
// c = new MapTile(cx, cy, z);
|
||||
//
|
||||
// QuadTree.add(c);
|
||||
// mTiles.add(c);
|
||||
// }
|
||||
//
|
||||
// if (!c.isActive()) {
|
||||
// mJobList.add(c);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (fetchParent || (!fetchProxy && zdir > 0 && zoomLevel > 0)) {
|
||||
if (zdir > 0 && zoomLevel > 0) {
|
||||
// prefetch parent
|
||||
MapTile p = tile.rel.parent.tile;
|
||||
|
||||
if (p == null) {
|
||||
p = new MapTile(x >> 1, y >> 1, (byte) (zoomLevel - 1));
|
||||
|
||||
QuadTree.add(p);
|
||||
mTiles.add(p);
|
||||
mJobList.add(p);
|
||||
|
||||
} else if (!p.isActive()) {
|
||||
if (!mJobList.contains(p))
|
||||
mJobList.add(p);
|
||||
}
|
||||
}
|
||||
return tile;
|
||||
}
|
||||
|
||||
private static void clearTile(MapTile t) {
|
||||
|
||||
t.newData = false;
|
||||
t.isLoading = false;
|
||||
t.isReady = false;
|
||||
|
||||
LineRenderer.clear(t.lineLayers);
|
||||
PolygonRenderer.clear(t.polygonLayers);
|
||||
|
||||
t.labels = null;
|
||||
t.lineLayers = null;
|
||||
t.polygonLayers = null;
|
||||
|
||||
if (t.vbo != null) {
|
||||
GLRenderer.addVBO(t.vbo);
|
||||
t.vbo = null;
|
||||
}
|
||||
if (t.texture != null)
|
||||
t.texture.tile = null;
|
||||
|
||||
QuadTree.remove(t);
|
||||
}
|
||||
|
||||
private static void updateTileDistances(ArrayList<?> tiles, MapPosition mapPosition) {
|
||||
int h = (Tile.TILE_SIZE >> 1);
|
||||
byte zoom = mapPosition.zoomLevel;
|
||||
long x = (long) mapPosition.x;
|
||||
long y = (long) mapPosition.y;
|
||||
|
||||
int diff;
|
||||
long dx, dy;
|
||||
|
||||
// TODO this could need some fixing, and optimization
|
||||
// to consider move/zoom direction
|
||||
|
||||
for (int i = 0, n = tiles.size(); i < n; i++) {
|
||||
JobTile t = (JobTile) tiles.get(i);
|
||||
diff = (t.zoomLevel - zoom);
|
||||
|
||||
if (diff == 0) {
|
||||
dx = (t.pixelX + h) - x;
|
||||
dy = (t.pixelY + h) - y;
|
||||
// t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) *
|
||||
// 0.25f;
|
||||
t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * 0.25f;
|
||||
} else if (diff > 0) {
|
||||
// tile zoom level is child of current
|
||||
|
||||
if (diff < 3) {
|
||||
dx = ((t.pixelX + h) >> diff) - x;
|
||||
dy = ((t.pixelY + h) >> diff) - y;
|
||||
}
|
||||
else {
|
||||
dx = ((t.pixelX + h) >> (diff >> 1)) - x;
|
||||
dy = ((t.pixelY + h) >> (diff >> 1)) - y;
|
||||
}
|
||||
// t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy));
|
||||
t.distance = FloatMath.sqrt((dx * dx + dy * dy));
|
||||
|
||||
} else {
|
||||
// tile zoom level is parent of current
|
||||
dx = ((t.pixelX + h) << -diff) - x;
|
||||
dy = ((t.pixelY + h) << -diff) - y;
|
||||
|
||||
// t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) *
|
||||
// (-diff * 0.5f);
|
||||
t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * (-diff * 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void limitCache(MapPosition mapPosition, int remove) {
|
||||
int size = mTiles.size();
|
||||
|
||||
// remove orphaned tiles
|
||||
for (int i = 0; i < size;) {
|
||||
MapTile t = mTiles.get(i);
|
||||
// make sure tile cannot be used by GL or MapWorker Thread
|
||||
if (t.isLocked() || t.isActive()) {
|
||||
i++;
|
||||
} else {
|
||||
// Log.d(TAG, "remove empty tile" + t);
|
||||
clearTile(t);
|
||||
mTiles.remove(i);
|
||||
remove--;
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
if (remove <= 0)
|
||||
return;
|
||||
|
||||
updateTileDistances(mTiles, mapPosition);
|
||||
Collections.sort(mTiles);
|
||||
|
||||
for (int i = 1; i < remove; i++) {
|
||||
|
||||
MapTile t = mTiles.remove(size - i);
|
||||
|
||||
synchronized (t) {
|
||||
if (t.isLocked()) {
|
||||
// dont remove tile used by renderthread
|
||||
Log.d(TAG, "X not removing " + t
|
||||
// + " " + t.isLocked
|
||||
+ " " + t.distance);
|
||||
|
||||
mTiles.add(t);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t.isLoading) {
|
||||
// NOTE: if we add tile back then on next limitCache
|
||||
// the tile will be removed. clearTile could interfere with
|
||||
// MapGenerator. so clear in passTile() instead.
|
||||
// mTiles.add(t);
|
||||
t.isLoading = false;
|
||||
Log.d(TAG, "X cancel loading " + t + " " + t.distance);
|
||||
continue;
|
||||
}
|
||||
|
||||
clearTile(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void limitLoadQueue() {
|
||||
int size = mTilesLoaded.size();
|
||||
|
||||
if (size < MAX_TILES_IN_QUEUE)
|
||||
return;
|
||||
|
||||
synchronized (mTilesLoaded) {
|
||||
|
||||
// remove tiles uploaded to vbo
|
||||
for (int i = 0; i < size;) {
|
||||
MapTile t = mTilesLoaded.get(i);
|
||||
// rel == null means tile is already removed by limitCache
|
||||
if (!t.newData || t.rel == null) {
|
||||
mTilesLoaded.remove(i);
|
||||
size--;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (size < MAX_TILES_IN_QUEUE)
|
||||
return;
|
||||
|
||||
// Log.d(TAG, "queue: " + mTilesLoaded.size() + " " + size + " "
|
||||
// + (size - MAX_TILES_IN_QUEUE / 2));
|
||||
|
||||
// clear loaded but not used tiles
|
||||
for (int i = 0, n = size - MAX_TILES_IN_QUEUE / 2; i < n; n--) {
|
||||
|
||||
MapTile t = mTilesLoaded.get(i);
|
||||
|
||||
synchronized (t) {
|
||||
if (t.rel == null) {
|
||||
mTilesLoaded.remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t.isLocked()) {
|
||||
// Log.d(TAG, "keep unused tile data: " + t + " " +
|
||||
// t.isActive);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Log.d(TAG, "remove unused tile data: " + t);
|
||||
mTilesLoaded.remove(i);
|
||||
mTiles.remove(t);
|
||||
clearTile(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called from MapWorker Thread when tile is loaded by TileGenerator
|
||||
*
|
||||
* @param jobTile
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public synchronized boolean passTile(JobTile jobTile) {
|
||||
MapTile tile = (MapTile) jobTile;
|
||||
|
||||
if (!tile.isLoading) {
|
||||
// no one should be able to use this tile now, TileGenerator passed
|
||||
// it, GL-Thread does nothing until newdata is set.
|
||||
Log.d(TAG, "passTile: canceled " + tile);
|
||||
synchronized (mTilesLoaded) {
|
||||
mTilesLoaded.add(tile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
mRenderer.setVBO(tile);
|
||||
|
||||
if (tile.vbo == null) {
|
||||
Log.d(TAG, "no VBOs left for " + tile);
|
||||
tile.isLoading = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
tile.newData = true;
|
||||
tile.isLoading = false;
|
||||
|
||||
if (!MapView.debugFrameTime)
|
||||
requestRender();
|
||||
|
||||
synchronized (mTilesLoaded) {
|
||||
mTilesLoaded.add(tile);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setRenderTheme(RenderTheme t) {
|
||||
if (mRenderer != null)
|
||||
mRenderer.setRenderTheme(t);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
|
||||
Log.d(TAG, "onSizeChanged" + w + " " + h);
|
||||
mWidth = w;
|
||||
mHeight = h;
|
||||
|
||||
if (mWidth > 0 && mHeight > 0)
|
||||
mInitial = true;
|
||||
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
}
|
||||
|
||||
// private static void updateVisibleList(MapPosition mapPosition, int zdir)
|
||||
// {
|
||||
// double x = mapPosition.x;
|
||||
// double y = mapPosition.y;
|
||||
// byte zoomLevel = mapPosition.zoomLevel;
|
||||
// float scale = mapPosition.scale;
|
||||
//
|
||||
// double add = 1.0f / scale;
|
||||
// int offsetX = (int) ((mWidth >> 1) * add) + Tile.TILE_SIZE;
|
||||
// int offsetY = (int) ((mHeight >> 1) * add) + Tile.TILE_SIZE;
|
||||
//
|
||||
// long pixelRight = (long) x + offsetX;
|
||||
// long pixelBottom = (long) y + offsetY;
|
||||
// long pixelLeft = (long) x - offsetX;
|
||||
// long pixelTop = (long) y - offsetY;
|
||||
//
|
||||
// int tileLeft = MercatorProjection.pixelXToTileX(pixelLeft, zoomLevel);
|
||||
// int tileTop = MercatorProjection.pixelYToTileY(pixelTop, zoomLevel);
|
||||
// int tileRight = MercatorProjection.pixelXToTileX(pixelRight, zoomLevel);
|
||||
// int tileBottom = MercatorProjection.pixelYToTileY(pixelBottom,
|
||||
// zoomLevel);
|
||||
//
|
||||
// mJobList.clear();
|
||||
//
|
||||
// // set non processed tiles to isLoading == false
|
||||
// mMapView.addJobs(null);
|
||||
//
|
||||
// int tiles = 0;
|
||||
// int max = mCurrentTiles.tiles.length - 1;
|
||||
//
|
||||
// // boolean fetchChildren = false;
|
||||
// // boolean fetchParent = false;
|
||||
// // boolean fetchProxy = false;
|
||||
// // if (mZoomLevels != null) {
|
||||
// // // check MapDatabase zoom-level-mapping
|
||||
// // if (mZoomLevels[zoomLevel] == 0) {
|
||||
// // mCurrentTiles.cnt = 0;
|
||||
// // mCurrentTiles = GLRenderer.updateTiles(mCurrentTiles);
|
||||
// // return;
|
||||
// // }
|
||||
// //
|
||||
// // if (mZoomLevels[zoomLevel] > zoomLevel) {
|
||||
// // fetchChildren = true;
|
||||
// // fetchProxy = true;
|
||||
// //
|
||||
// // } else if (mZoomLevels[zoomLevel] < zoomLevel) {
|
||||
// // fetchParent = true;
|
||||
// // fetchProxy = true;
|
||||
// // }
|
||||
// // }
|
||||
//
|
||||
// for (int yy = tileTop; yy <= tileBottom; yy++) {
|
||||
// for (int xx = tileLeft; xx <= tileRight; xx++) {
|
||||
//
|
||||
// if (tiles == max)
|
||||
// break;
|
||||
//
|
||||
// // MapTile tile =
|
||||
// addTile(xx, yy, zoomLevel, zdir);
|
||||
// // mCurrentTiles.tiles[tiles++] = tile;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // pass new tile list to glThread
|
||||
// mCurrentTiles.cnt = tiles;
|
||||
// mCurrentTiles = GLRenderer.updateTiles(mCurrentTiles);
|
||||
//
|
||||
// // note: this sets isLoading == true for all job tiles
|
||||
// if (mJobList.size() > 0) {
|
||||
// updateTileDistances(mJobList, mapPosition);
|
||||
// Collections.sort(mJobList);
|
||||
// mMapView.addJobs(mJobList);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.renderer;
|
||||
|
||||
import org.oscim.view.generator.JobTile;
|
||||
|
||||
class MapTile extends JobTile {
|
||||
|
||||
/**
|
||||
* VBO layout: - 16 bytes fill coordinates, n bytes polygon vertices, m
|
||||
* bytes lines vertices
|
||||
*/
|
||||
VertexBufferObject vbo;
|
||||
|
||||
/**
|
||||
* polygonOffset in vbo is always 16 bytes,
|
||||
*/
|
||||
int lineOffset;
|
||||
|
||||
TextTexture texture;
|
||||
|
||||
/**
|
||||
* Tile data set by TileGenerator:
|
||||
*/
|
||||
LineLayer lineLayers;
|
||||
PolygonLayer polygonLayers;
|
||||
TextItem labels;
|
||||
|
||||
/**
|
||||
* tile is used by render thread. set by updateVisibleList (main thread).
|
||||
*/
|
||||
// boolean isLocked;
|
||||
|
||||
/**
|
||||
* tile has new data to upload to gl
|
||||
*/
|
||||
boolean newData;
|
||||
|
||||
/**
|
||||
* tile is loaded and ready for drawing.
|
||||
*/
|
||||
boolean isReady;
|
||||
|
||||
/**
|
||||
* tile is in view region.
|
||||
*/
|
||||
boolean isVisible;
|
||||
|
||||
/**
|
||||
* pointer to access relatives in TileTree
|
||||
*/
|
||||
QuadTree rel;
|
||||
|
||||
int lastDraw = 0;
|
||||
|
||||
// keep track which tiles are locked as proxy for this tile
|
||||
final static int PROXY_PARENT = 16;
|
||||
final static int PROXY_GRAMPA = 32;
|
||||
final static int PROXY_HOLDER = 64;
|
||||
// 1-8: children
|
||||
byte proxies;
|
||||
|
||||
// counting the tiles that use this tile as proxy
|
||||
byte refs;
|
||||
|
||||
byte locked;
|
||||
|
||||
// this tile sits in fo another tile. e.g. x:-1,y:0,z:1 for x:1,y:0
|
||||
MapTile holder;
|
||||
|
||||
boolean isActive() {
|
||||
return isLoading || newData || isReady;
|
||||
}
|
||||
|
||||
boolean isLocked() {
|
||||
return locked > 0 || refs > 0;
|
||||
}
|
||||
|
||||
void lock() {
|
||||
if (holder != null)
|
||||
return;
|
||||
|
||||
locked++;
|
||||
|
||||
if (locked > 1 || isReady || newData)
|
||||
return;
|
||||
|
||||
MapTile p = rel.parent.tile;
|
||||
|
||||
if (p != null && (p.isReady || p.newData || p.isLoading)) {
|
||||
proxies |= PROXY_PARENT;
|
||||
p.refs++;
|
||||
}
|
||||
|
||||
p = rel.parent.parent.tile;
|
||||
if (p != null && (p.isReady || p.newData || p.isLoading)) {
|
||||
proxies |= PROXY_GRAMPA;
|
||||
p.refs++;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 4; j++) {
|
||||
if (rel.child[j] != null) {
|
||||
p = rel.child[j].tile;
|
||||
if (p != null && (p.isReady || p.newData || p.isLoading)) {
|
||||
proxies |= (1 << j);
|
||||
p.refs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
if (holder != null)
|
||||
return;
|
||||
|
||||
locked--;
|
||||
|
||||
if (locked > 0 || proxies == 0)
|
||||
return;
|
||||
|
||||
if ((proxies & (1 << 4)) != 0) {
|
||||
MapTile p = rel.parent.tile;
|
||||
p.refs--;
|
||||
}
|
||||
if ((proxies & (1 << 5)) != 0) {
|
||||
MapTile p = rel.parent.parent.tile;
|
||||
p.refs--;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ((proxies & (1 << i)) != 0) {
|
||||
rel.child[i].tile.refs--;
|
||||
}
|
||||
}
|
||||
proxies = 0;
|
||||
}
|
||||
|
||||
MapTile(int tileX, int tileY, byte zoomLevel) {
|
||||
super(tileX, tileY, zoomLevel);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.renderer;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.theme.renderinstruction.Area;
|
||||
|
||||
class PolygonLayer {
|
||||
private static final float S = GLRenderer.COORD_MULTIPLIER;
|
||||
|
||||
PolygonLayer next;
|
||||
Area area;
|
||||
|
||||
VertexPoolItem pool;
|
||||
protected VertexPoolItem curItem;
|
||||
int verticesCnt;
|
||||
int offset;
|
||||
|
||||
final int layer;
|
||||
|
||||
PolygonLayer(int layer, Area area) {
|
||||
this.layer = layer;
|
||||
this.area = area;
|
||||
curItem = VertexPool.get();
|
||||
pool = curItem;
|
||||
}
|
||||
|
||||
void addPolygon(float[] points, short[] index) {
|
||||
short center = (short) ((Tile.TILE_SIZE >> 1) * S);
|
||||
|
||||
VertexPoolItem si = curItem;
|
||||
short[] v = si.vertices;
|
||||
int outPos = si.used;
|
||||
|
||||
for (int i = 0, pos = 0, n = index.length; i < n; i++) {
|
||||
int length = index[i];
|
||||
if (length < 0)
|
||||
break;
|
||||
|
||||
// need at least three points
|
||||
if (length < 6) {
|
||||
pos += length;
|
||||
continue;
|
||||
}
|
||||
|
||||
verticesCnt += length / 2 + 2;
|
||||
|
||||
int inPos = pos;
|
||||
|
||||
if (outPos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
outPos = 0;
|
||||
}
|
||||
|
||||
v[outPos++] = center;
|
||||
v[outPos++] = center;
|
||||
|
||||
for (int j = 0; j < length; j += 2) {
|
||||
if (outPos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
outPos = 0;
|
||||
}
|
||||
v[outPos++] = (short) (points[inPos++] * S);
|
||||
v[outPos++] = (short) (points[inPos++] * S);
|
||||
}
|
||||
|
||||
if (outPos == VertexPoolItem.SIZE) {
|
||||
si = si.next = VertexPool.get();
|
||||
v = si.vertices;
|
||||
outPos = 0;
|
||||
}
|
||||
|
||||
v[outPos++] = (short) (points[pos + 0] * S);
|
||||
v[outPos++] = (short) (points[pos + 1] * S);
|
||||
|
||||
pos += length;
|
||||
}
|
||||
|
||||
si.used = outPos;
|
||||
curItem = si;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,376 +0,0 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.renderer;
|
||||
|
||||
import static android.opengl.GLES20.GL_BLEND;
|
||||
import static android.opengl.GLES20.GL_EQUAL;
|
||||
import static android.opengl.GLES20.GL_INVERT;
|
||||
import static android.opengl.GLES20.GL_STENCIL_TEST;
|
||||
import static android.opengl.GLES20.GL_TRIANGLE_FAN;
|
||||
import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
|
||||
import static android.opengl.GLES20.GL_ZERO;
|
||||
import static android.opengl.GLES20.glColorMask;
|
||||
import static android.opengl.GLES20.glDisable;
|
||||
import static android.opengl.GLES20.glDrawArrays;
|
||||
import static android.opengl.GLES20.glEnable;
|
||||
import static android.opengl.GLES20.glGetAttribLocation;
|
||||
import static android.opengl.GLES20.glGetUniformLocation;
|
||||
import static android.opengl.GLES20.glStencilFunc;
|
||||
import static android.opengl.GLES20.glStencilMask;
|
||||
import static android.opengl.GLES20.glStencilOp;
|
||||
import static android.opengl.GLES20.glUniform4fv;
|
||||
import static android.opengl.GLES20.glUniformMatrix4fv;
|
||||
import static android.opengl.GLES20.glUseProgram;
|
||||
import static android.opengl.GLES20.glVertexAttribPointer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import org.oscim.utils.GlUtils;
|
||||
import org.oscim.view.MapPosition;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
|
||||
class PolygonRenderer {
|
||||
// private static final String TAG = "PolygonRenderer";
|
||||
|
||||
private static final int NUM_VERTEX_SHORTS = 2;
|
||||
private static final int POLYGON_VERTICES_DATA_POS_OFFSET = 0;
|
||||
private static final int STENCIL_BITS = 8;
|
||||
|
||||
private static final float FADE_START = 1.3f;
|
||||
|
||||
private static PolygonLayer[] mFillPolys;
|
||||
|
||||
private static int polygonProgram;
|
||||
private static int hPolygonVertexPosition;
|
||||
private static int hPolygonMatrix;
|
||||
private static int hPolygonColor;
|
||||
|
||||
static boolean init() {
|
||||
|
||||
// Set up the program for rendering polygons
|
||||
polygonProgram = GlUtils.createProgram(Shaders.polygonVertexShader,
|
||||
Shaders.polygonFragmentShader);
|
||||
if (polygonProgram == 0) {
|
||||
// Log.e(TAG, "Could not create polygon program.");
|
||||
return false;
|
||||
}
|
||||
hPolygonMatrix = glGetUniformLocation(polygonProgram, "u_mvp");
|
||||
hPolygonColor = glGetUniformLocation(polygonProgram, "u_color");
|
||||
hPolygonVertexPosition = glGetAttribLocation(polygonProgram, "a_position");
|
||||
|
||||
mFillPolys = new PolygonLayer[STENCIL_BITS];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void fillPolygons(int zoom, float scale) {
|
||||
boolean blend = false;
|
||||
|
||||
/* draw to framebuffer */
|
||||
glColorMask(true, true, true, true);
|
||||
|
||||
/* do not modify stencil buffer */
|
||||
glStencilMask(0);
|
||||
|
||||
/* only draw where nothing was drawn yet */
|
||||
glEnable(GLES20.GL_DEPTH_TEST);
|
||||
|
||||
for (int c = mStart; c < mCount; c++) {
|
||||
PolygonLayer l = mFillPolys[c];
|
||||
|
||||
float f = 1.0f;
|
||||
|
||||
if (l.area.fade >= zoom || l.area.color[3] != 1.0) {
|
||||
/* fade in/out || draw alpha color */
|
||||
if (l.area.fade >= zoom) {
|
||||
f = (scale > FADE_START ? scale : FADE_START) - f;
|
||||
if (f > 1.0f)
|
||||
f = 1.0f;
|
||||
}
|
||||
|
||||
f *= l.area.color[3];
|
||||
|
||||
if (!blend) {
|
||||
glEnable(GL_BLEND);
|
||||
blend = true;
|
||||
}
|
||||
|
||||
GlUtils.setColor(hPolygonColor, l.area.color, f);
|
||||
|
||||
} else if (l.area.blend == zoom) {
|
||||
/* blend colors */
|
||||
f = scale - 1.0f;
|
||||
if (f > 1.0f)
|
||||
f = 1.0f;
|
||||
else if (f < 0)
|
||||
f = 0;
|
||||
|
||||
GlUtils.setBlendColors(hPolygonColor,
|
||||
l.area.color, l.area.blendColor, f);
|
||||
|
||||
} else {
|
||||
/* draw solid */
|
||||
if (blend) {
|
||||
glDisable(GL_BLEND);
|
||||
blend = false;
|
||||
}
|
||||
if (l.area.blend <= zoom && l.area.blend > 0)
|
||||
glUniform4fv(hPolygonColor, 1, l.area.blendColor, 0);
|
||||
else
|
||||
glUniform4fv(hPolygonColor, 1, l.area.color, 0);
|
||||
}
|
||||
|
||||
// if (alpha < 1) {
|
||||
// if (!blend) {
|
||||
// glEnable(GL_BLEND);
|
||||
// blend = true;
|
||||
// }
|
||||
// } else if (blend) {
|
||||
// glDisable(GL_BLEND);
|
||||
// blend = false;
|
||||
// }
|
||||
|
||||
/* set stencil buffer mask used to draw this layer */
|
||||
glStencilFunc(GL_EQUAL, 0xff, 1 << c);
|
||||
|
||||
/* draw tile fill coordinates */
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
if (blend)
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
// layers to fill
|
||||
private static int mCount;
|
||||
// stencil buffer index to start fill
|
||||
private static int mStart;
|
||||
|
||||
static PolygonLayer drawPolygons(MapPosition pos, PolygonLayer layer, int next,
|
||||
float[] matrix, boolean first) {
|
||||
|
||||
int zoom = pos.zoomLevel;
|
||||
float scale = pos.scale;
|
||||
|
||||
glUseProgram(polygonProgram);
|
||||
GLES20.glEnableVertexAttribArray(hPolygonVertexPosition);
|
||||
|
||||
glVertexAttribPointer(hPolygonVertexPosition, 2, GLES20.GL_SHORT,
|
||||
false, 0, POLYGON_VERTICES_DATA_POS_OFFSET);
|
||||
|
||||
glUniformMatrix4fv(hPolygonMatrix, 1, false, matrix, 0);
|
||||
|
||||
// use stencilbuffer method for polygon drawing
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
|
||||
PolygonLayer l = layer;
|
||||
|
||||
if (first) {
|
||||
mCount = 0;
|
||||
mStart = 0;
|
||||
} else {
|
||||
mStart = mCount;
|
||||
}
|
||||
|
||||
for (; l != null && l.layer < next; l = l.next) {
|
||||
// fade out polygon layers (set in RederTheme)
|
||||
if (l.area.fade > 0 && l.area.fade > zoom)
|
||||
continue;
|
||||
|
||||
if (mCount == 0) {
|
||||
// clear stencilbuffer (tile region)
|
||||
|
||||
// disable drawing to framebuffer
|
||||
glColorMask(false, false, false, false);
|
||||
|
||||
// never pass the test: always apply fail op
|
||||
glStencilFunc(GLES20.GL_ALWAYS, 0, 0xFF);
|
||||
glStencilMask(0xFF);
|
||||
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
|
||||
|
||||
if (first) {
|
||||
// draw clip-region into depth buffer:
|
||||
// this is used for lines and polygons
|
||||
|
||||
// write to depth buffer
|
||||
GLES20.glDepthMask(true);
|
||||
|
||||
// to prevent overdraw gl_less restricts
|
||||
// the clip to the area where no other
|
||||
// tile was drawn
|
||||
GLES20.glDepthFunc(GLES20.GL_LESS);
|
||||
}
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
// dont modify depth buffer
|
||||
GLES20.glDepthMask(false);
|
||||
// only draw to this tile
|
||||
GLES20.glDepthFunc(GLES20.GL_EQUAL);
|
||||
}
|
||||
|
||||
// stencil op for stencil method polygon drawing
|
||||
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
|
||||
glDisable(GLES20.GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
mFillPolys[mCount] = l;
|
||||
|
||||
// set stencil mask to draw to
|
||||
glStencilMask(1 << mCount++);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, l.offset, l.verticesCnt);
|
||||
|
||||
// draw up to 8 layers into stencil buffer
|
||||
if (mCount == STENCIL_BITS) {
|
||||
fillPolygons(zoom, scale);
|
||||
mCount = 0;
|
||||
mStart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (mCount > 0)
|
||||
fillPolygons(zoom, scale);
|
||||
|
||||
// maybe reset start when only few layers left in stencil buffer
|
||||
// if (mCount > 5){
|
||||
// mCount = 0;
|
||||
// mStart = 0;
|
||||
// }
|
||||
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
if (first)
|
||||
drawDepthClip();
|
||||
|
||||
GLES20.glDisableVertexAttribArray(hPolygonVertexPosition);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
private static float[] debugFillColor = { 0.3f, 0.0f, 0.0f, 0.3f };
|
||||
|
||||
private static ByteBuffer mDebugFill;
|
||||
|
||||
static void debugDraw(float[] matrix, float[] coords) {
|
||||
|
||||
mDebugFill = ByteBuffer.allocateDirect(32).order(ByteOrder.nativeOrder());
|
||||
FloatBuffer buf = mDebugFill.asFloatBuffer();
|
||||
buf.put(coords);
|
||||
|
||||
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
mDebugFill.position(0);
|
||||
glUseProgram(polygonProgram);
|
||||
GLES20.glEnableVertexAttribArray(hPolygonVertexPosition);
|
||||
|
||||
glVertexAttribPointer(hPolygonVertexPosition, 2, GLES20.GL_FLOAT,
|
||||
false, 0, mDebugFill);
|
||||
|
||||
glUniformMatrix4fv(hPolygonMatrix, 1, false, matrix, 0);
|
||||
|
||||
glUniform4fv(hPolygonColor, 1, debugFillColor, 0);
|
||||
|
||||
glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
GlUtils.checkGlError("draw debug");
|
||||
GLES20.glDisableVertexAttribArray(hPolygonVertexPosition);
|
||||
}
|
||||
|
||||
static void drawDepthClip() {
|
||||
glColorMask(false, false, false, false);
|
||||
GLES20.glDepthMask(true);
|
||||
GLES20.glDepthFunc(GLES20.GL_LESS);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
GLES20.glDepthMask(false);
|
||||
glColorMask(true, true, true, true);
|
||||
GLES20.glDepthFunc(GLES20.GL_EQUAL);
|
||||
}
|
||||
|
||||
static int sizeOf(PolygonLayer layers) {
|
||||
int size = 0;
|
||||
|
||||
for (PolygonLayer l = layers; l != null; l = l.next)
|
||||
size += l.verticesCnt;
|
||||
|
||||
size *= NUM_VERTEX_SHORTS;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void compileLayerData(PolygonLayer layers, ShortBuffer sbuf) {
|
||||
int pos = 4;
|
||||
|
||||
VertexPoolItem last = null, items = null;
|
||||
|
||||
for (PolygonLayer l = layers; l != null; l = l.next) {
|
||||
|
||||
for (VertexPoolItem item = l.pool; item != null; item = item.next) {
|
||||
|
||||
if (item.next == null) {
|
||||
sbuf.put(item.vertices, 0, item.used);
|
||||
} else {
|
||||
// item.used = VertexPoolItem.SIZE;
|
||||
sbuf.put(item.vertices);
|
||||
}
|
||||
|
||||
last = item;
|
||||
}
|
||||
|
||||
l.offset = pos;
|
||||
pos += l.verticesCnt;
|
||||
|
||||
if (last != null) {
|
||||
last.next = items;
|
||||
items = l.pool;
|
||||
}
|
||||
|
||||
l.pool = null;
|
||||
}
|
||||
|
||||
VertexPool.add(items);
|
||||
}
|
||||
|
||||
static void clear(PolygonLayer layers) {
|
||||
for (PolygonLayer l = layers; l != null; l = l.next) {
|
||||
if (l.pool != null)
|
||||
VertexPool.add(l.pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* 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.renderer;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
class QuadTree {
|
||||
private static String TAG = "QuadTree";
|
||||
|
||||
// pointer to tile 0/0/0
|
||||
private static QuadTree root;
|
||||
|
||||
// parent pointer is used to link pool items
|
||||
private static QuadTree pool;
|
||||
|
||||
QuadTree parent;
|
||||
// .... x y
|
||||
// 0 => 0 0
|
||||
// 1 => 1 0
|
||||
// 2 => 0 1
|
||||
// 3 => 1 1
|
||||
final QuadTree[] child = new QuadTree[4];
|
||||
int refs = 0;
|
||||
byte id;
|
||||
MapTile tile;
|
||||
|
||||
static void init() {
|
||||
|
||||
pool = null;
|
||||
root = new QuadTree();
|
||||
root.parent = root;
|
||||
|
||||
// QuadTree t;
|
||||
// for (int i = 0; i < 200; i++) {
|
||||
// t = new QuadTree();
|
||||
// t.parent = pool;
|
||||
// pool = t;
|
||||
// }
|
||||
}
|
||||
|
||||
static boolean remove(MapTile t) {
|
||||
if (t.rel == null) {
|
||||
Log.d(TAG, "already removed " + t);
|
||||
return true;
|
||||
}
|
||||
|
||||
QuadTree cur = t.rel;
|
||||
QuadTree next;
|
||||
|
||||
for (; cur != root;) {
|
||||
// keep pointer to parent
|
||||
next = cur.parent;
|
||||
cur.refs--;
|
||||
|
||||
// if current node has no children
|
||||
if (cur.refs == 0) {
|
||||
// unhook from parent
|
||||
next.child[cur.id] = null;
|
||||
|
||||
// add item back to pool
|
||||
cur.parent = pool;
|
||||
pool = cur;
|
||||
}
|
||||
cur = next;
|
||||
}
|
||||
|
||||
root.refs--;
|
||||
|
||||
t.rel.tile = null;
|
||||
t.rel = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static QuadTree add(MapTile tile) {
|
||||
|
||||
int x = tile.tileX;
|
||||
int y = tile.tileY;
|
||||
int z = tile.zoomLevel;
|
||||
|
||||
QuadTree cur;
|
||||
|
||||
// if (x < 0 || x >= 1 << z) {
|
||||
// Log.d(TAG, "invalid position");
|
||||
// return null;
|
||||
// }
|
||||
// if (y < 0 || y >= 1 << z) {
|
||||
// Log.d(TAG, "invalid position");
|
||||
// return null;
|
||||
// }
|
||||
|
||||
QuadTree leaf = root;
|
||||
|
||||
for (int level = z - 1; level >= 0; level--) {
|
||||
|
||||
int id = ((x >> level) & 1) | ((y >> level) & 1) << 1;
|
||||
|
||||
leaf.refs++;
|
||||
|
||||
cur = leaf.child[id];
|
||||
|
||||
if (cur != null) {
|
||||
leaf = cur;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pool != null) {
|
||||
cur = pool;
|
||||
pool = pool.parent;
|
||||
} else {
|
||||
cur = new QuadTree();
|
||||
}
|
||||
|
||||
cur.refs = 0;
|
||||
cur.id = (byte) id;
|
||||
cur.parent = leaf;
|
||||
cur.parent.child[id] = cur;
|
||||
|
||||
leaf = cur;
|
||||
}
|
||||
|
||||
leaf.refs++;
|
||||
leaf.tile = tile;
|
||||
tile.rel = leaf;
|
||||
|
||||
return leaf;
|
||||
}
|
||||
|
||||
static MapTile getTile(int x, int y, int z) {
|
||||
QuadTree leaf = root;
|
||||
|
||||
for (int level = z - 1; level >= 0; level--) {
|
||||
|
||||
leaf = leaf.child[((x >> level) & 1) | ((y >> level) & 1) << 1];
|
||||
|
||||
if (leaf == null)
|
||||
return null;
|
||||
|
||||
if (level == 0) {
|
||||
return leaf.tile;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* ported from Polymaps: Layer.js Copyright (c) 2010, SimpleGeo and Stamen Design */
|
||||
|
||||
package org.oscim.view.renderer;
|
||||
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
public abstract class ScanBox {
|
||||
|
||||
static class Edge {
|
||||
float x0, y0, x1, y1, dx, dy;
|
||||
|
||||
void set(float x0, float y0, float x1, float y1) {
|
||||
if (y0 <= y1) {
|
||||
this.x0 = x0;
|
||||
this.y0 = y0;
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
dx = x1 - x0;
|
||||
dy = y1 - y0;
|
||||
} else {
|
||||
this.x0 = x1;
|
||||
this.y0 = y1;
|
||||
this.x1 = x0;
|
||||
this.y1 = y0;
|
||||
dx = x0 - x1;
|
||||
dy = y0 - y1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Edge ab = new Edge();
|
||||
private Edge bc = new Edge();
|
||||
private Edge ca = new Edge();
|
||||
protected byte mZoom;
|
||||
|
||||
void scan(float[] coords, byte zoom) {
|
||||
mZoom = zoom;
|
||||
|
||||
ab.set(coords[0], coords[1], coords[2], coords[3]);
|
||||
bc.set(coords[2], coords[3], coords[4], coords[5]);
|
||||
ca.set(coords[4], coords[5], coords[0], coords[1]);
|
||||
scanTriangle();
|
||||
|
||||
ab.set(coords[4], coords[5], coords[6], coords[7]);
|
||||
bc.set(coords[6], coords[7], coords[0], coords[1]);
|
||||
ca.set(coords[0], coords[1], coords[4], coords[5]);
|
||||
scanTriangle();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param y
|
||||
* ...
|
||||
* @param x1
|
||||
* ...
|
||||
* @param x2
|
||||
* ...
|
||||
*/
|
||||
void setVisible(int y, int x1, int x2) {
|
||||
}
|
||||
|
||||
private void scanTriangle() {
|
||||
|
||||
if (ab.dy > bc.dy) {
|
||||
Edge t = ab;
|
||||
ab = bc;
|
||||
bc = t;
|
||||
}
|
||||
if (ab.dy > ca.dy) {
|
||||
Edge t = ab;
|
||||
ab = ca;
|
||||
ca = t;
|
||||
}
|
||||
if (bc.dy > ca.dy) {
|
||||
Edge t = bc;
|
||||
bc = ca;
|
||||
ca = t;
|
||||
}
|
||||
// ca.dy > bc.dy > ab.dy
|
||||
|
||||
if (ca.dy == 0)
|
||||
return;
|
||||
|
||||
if (ab.dy != 0)
|
||||
scanSpans(ca, ab);
|
||||
|
||||
if (bc.dy != 0)
|
||||
scanSpans(ca, bc);
|
||||
}
|
||||
|
||||
private static final int MAX_SLOPE = 4;
|
||||
|
||||
private void scanSpans(Edge e0, Edge e1) {
|
||||
|
||||
int y0 = (int) Math.max(0, FloatMath.floor(e1.y0));
|
||||
int y1 = (int) Math.min((1 << mZoom), FloatMath.ceil(e1.y1));
|
||||
|
||||
// sort edge by x-coordinate
|
||||
if (e0.x0 == e1.x0 && e0.y0 == e1.y0) {
|
||||
// bottom-flat
|
||||
if (e0.x0 + e1.dy / e0.dy * e0.dx < e1.x1) {
|
||||
Edge t = e0;
|
||||
e0 = e1;
|
||||
e1 = t;
|
||||
}
|
||||
} else {
|
||||
// top-flat
|
||||
if (e0.x1 - e1.dy / e0.dy * e0.dx < e1.x0) {
|
||||
Edge t = e0;
|
||||
e0 = e1;
|
||||
e1 = t;
|
||||
}
|
||||
}
|
||||
|
||||
float m0 = e0.dx / e0.dy;
|
||||
float m1 = e1.dx / e1.dy;
|
||||
|
||||
// still needed?
|
||||
if (m0 > MAX_SLOPE)
|
||||
m0 = MAX_SLOPE;
|
||||
else if (m0 < -MAX_SLOPE)
|
||||
m0 = -MAX_SLOPE;
|
||||
|
||||
if (m1 > MAX_SLOPE)
|
||||
m1 = MAX_SLOPE;
|
||||
else if (m1 < -MAX_SLOPE)
|
||||
m1 = -MAX_SLOPE;
|
||||
|
||||
int d0 = e0.dx > 0 ? 1 : 0; // use y + 1 to compute x0
|
||||
int d1 = e1.dx < 0 ? 1 : 0; // use y + 1 to compute x1
|
||||
|
||||
float x0, x1, dy;
|
||||
|
||||
for (int y = y0; y < y1; y++) {
|
||||
dy = y + d0 - e0.y0;
|
||||
if (e0.dy < dy)
|
||||
dy = e0.dy;
|
||||
|
||||
x0 = e0.x0 + m0 * dy;
|
||||
x0 = FloatMath.ceil(x0);
|
||||
|
||||
dy = y + d1 - e1.y0;
|
||||
if (e1.dy < dy)
|
||||
dy = e1.dy;
|
||||
|
||||
x1 = e1.x0 + m1 * dy;
|
||||
x1 = FloatMath.floor(x1);
|
||||
|
||||
if (x1 > x0)
|
||||
Log.d("...", "X set visible" + y + " " + x1 + "/" + x0);
|
||||
|
||||
setVisible(y, (int) x1, (int) x0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oscim.view.renderer;
|
||||
|
||||
class Shaders {
|
||||
|
||||
final static String lineVertexShader = ""
|
||||
+ "precision mediump float;"
|
||||
+ "uniform mat4 u_mvp;"
|
||||
+ "uniform float u_width;"
|
||||
+ "attribute vec2 a_position;"
|
||||
+ "attribute vec2 a_st;"
|
||||
+ "varying vec2 v_st;"
|
||||
+ "const float dscale = 8.0/2048.0;"
|
||||
+ "void main() {"
|
||||
// scale extrusion to u_width pixel
|
||||
// just ignore the two most insignificant bits of a_st :)
|
||||
+ " vec2 dir = dscale * u_width * a_st;"
|
||||
+ " gl_Position = u_mvp * vec4(a_position + dir, 0.0,1.0);"
|
||||
// last two bits of a_st hold the texture coordinates
|
||||
+ " v_st = u_width * (abs(mod(a_st,4.0)) - 1.0);"
|
||||
// TODO use bit operations when available (gles 1.3)
|
||||
// + " v_st = u_width * vec2(ivec2(a_st) & 3 - 1);"
|
||||
+ "}";
|
||||
|
||||
// final static String lineVertexShader = ""
|
||||
// + "precision mediump float;"
|
||||
// + "uniform mat4 mvp;"
|
||||
// + "attribute vec4 a_position;"
|
||||
// + "attribute vec2 a_st;"
|
||||
// + "varying vec2 v_st;"
|
||||
// + "uniform float u_width;"
|
||||
// + "const float dscale = 8.0/1000.0;"
|
||||
// + "void main() {"
|
||||
// + " vec2 dir = dscale * u_width * a_position.zw;"
|
||||
// + " gl_Position = mvp * vec4(a_position.xy + dir, 0.0,1.0);"
|
||||
// + " v_st = u_width * a_st;"
|
||||
// + "}";
|
||||
|
||||
final static String lineSimpleFragmentShader = ""
|
||||
+ "precision mediump float;"
|
||||
+ "uniform float u_wscale;"
|
||||
+ "uniform float u_width;"
|
||||
+ "uniform vec4 u_color;"
|
||||
+ "varying vec2 v_st;"
|
||||
+ "const float zero = 0.0;"
|
||||
+ "void main() {"
|
||||
+ " float len;"
|
||||
+ " if (v_st.t == zero)"
|
||||
+ " len = abs(v_st.s);"
|
||||
+ " else "
|
||||
+ " len = length(v_st);"
|
||||
// fade to alpha. u_wscale is the width in pixel which should be
|
||||
// faded, u_width - len the position of this fragment on the
|
||||
// perpendicular to this line segment
|
||||
+ " gl_FragColor = smoothstep(zero, u_wscale, u_width - len) * u_color;"
|
||||
+ "}";
|
||||
|
||||
final static String lineFragmentShader = ""
|
||||
+ "#extension GL_OES_standard_derivatives : enable\n"
|
||||
+ "precision mediump float;\n"
|
||||
+ "uniform float u_wscale;"
|
||||
+ "uniform float u_width;"
|
||||
+ "uniform vec4 u_color;"
|
||||
+ "varying vec2 v_st;"
|
||||
+ "const float zero = 0.0;"
|
||||
+ "void main() {"
|
||||
+ " vec4 color = u_color;"
|
||||
+ " float width = u_width;"
|
||||
+ " float len;"
|
||||
+ " if (v_st.t == zero)"
|
||||
+ " len = abs(v_st.s);"
|
||||
+ " else "
|
||||
+ " len = length(v_st);"
|
||||
+ " vec2 st_width = fwidth(v_st);"
|
||||
+ " float fuzz = max(st_width.s, st_width.t) * 1.5;"
|
||||
+ " color *= smoothstep(zero, fuzz + u_wscale, u_width - len);"
|
||||
+ " gl_FragColor = color;"
|
||||
+ "}";
|
||||
|
||||
final static String polygonVertexShader = ""
|
||||
+ "precision mediump float;"
|
||||
+ "uniform mat4 u_mvp;"
|
||||
+ "attribute vec4 a_position;"
|
||||
+ "void main() {"
|
||||
+ " gl_Position = u_mvp * a_position;"
|
||||
+ "}";
|
||||
|
||||
final static String polygonFragmentShader = ""
|
||||
+ "precision mediump float;"
|
||||
+ "uniform vec4 u_color;"
|
||||
+ "void main() {"
|
||||
+ " gl_FragColor = u_color;"
|
||||
+ "}";
|
||||
|
||||
final static String textVertexShader = ""
|
||||
+ "precision highp float; "
|
||||
+ "attribute vec4 vertex;"
|
||||
+ "attribute vec2 tex_coord;"
|
||||
+ "uniform mat4 u_mv;"
|
||||
+ "uniform mat4 u_proj;"
|
||||
+ "uniform float u_scale;"
|
||||
+ "varying vec2 tex_c;"
|
||||
+ "const vec2 div = vec2(1.0/4096.0,1.0/2048.0);"
|
||||
+ "const float coord_scale = 0.125;"
|
||||
+ "void main() {"
|
||||
+ " vec4 pos;"
|
||||
+ " if (mod(vertex.x, 2.0) == 0.0){"
|
||||
+ " pos = u_proj * (u_mv * vec4(vertex.xy + vertex.zw * u_scale, 0.0, 1.0));"
|
||||
+ " } else {"
|
||||
// place as billboard
|
||||
+ " vec4 dir = u_mv * vec4(vertex.xy, 0.0, 1.0);"
|
||||
+ " pos = u_proj * (dir + vec4(vertex.zw * coord_scale, 0.0, 0.0));"
|
||||
+ " }"
|
||||
+ " gl_Position = pos;"
|
||||
+ " tex_c = tex_coord * div;"
|
||||
+ "}";
|
||||
|
||||
// final static String textVertexShader = ""
|
||||
// + "precision highp float; "
|
||||
// + "attribute vec4 vertex;"
|
||||
// + "attribute vec2 tex_coord;"
|
||||
// + "uniform mat4 mvp;"
|
||||
// + "uniform mat4 rotation;"
|
||||
// + "uniform float scale;"
|
||||
// + "varying vec2 tex_c;"
|
||||
// + "const vec2 div = vec2(1.0/4096.0,1.0/2048.0);"
|
||||
// + "void main() {"
|
||||
// + " vec4 pos;"
|
||||
// + " if (mod(vertex.x, 2.0) == 0.0){"
|
||||
// + " pos = mvp * vec4(vertex.xy + vertex.zw / scale, 0.0, 1.0);"
|
||||
// + " } else {"
|
||||
// + " vec4 dir = rotation * vec4(vertex.zw / scale, 0.0, 1.0);"
|
||||
// + " pos = mvp * vec4(vertex.xy + dir.xy, 0.0, 1.0);"
|
||||
// + " }"
|
||||
// + " pos.z = 0.0;"
|
||||
// + " gl_Position = pos;"
|
||||
// + " tex_c = tex_coord * div;"
|
||||
// + "}";
|
||||
|
||||
// final static String textVertexShader = ""
|
||||
// + "precision highp float; "
|
||||
// + "attribute vec4 vertex;"
|
||||
// + "attribute vec2 tex_coord;"
|
||||
// + "uniform mat4 mvp;"
|
||||
// + "uniform mat4 rotation;"
|
||||
// + "uniform float scale;"
|
||||
// + "varying vec2 tex_c;"
|
||||
// + "const vec2 div = vec2(1.0/4096.0,1.0/2048.0);"
|
||||
// + "void main() {"
|
||||
// + " if (mod(vertex.x, 2.0) == 0.0){"
|
||||
// +
|
||||
// " gl_Position = mvp * vec4(vertex.xy + vertex.zw / scale, 0.0, 1.0);"
|
||||
// + " } else {"
|
||||
// + " vec4 dir = rotation * vec4(vertex.zw / scale, 0.0, 1.0);"
|
||||
// + " gl_Position = mvp * vec4(vertex.xy + dir.xy, 0.0, 1.0);"
|
||||
// + " }"
|
||||
// + " tex_c = tex_coord * div;"
|
||||
// + "}";
|
||||
|
||||
final static String textFragmentShader = ""
|
||||
+ "precision highp float;"
|
||||
+ "uniform sampler2D tex;"
|
||||
+ "varying vec2 tex_c;"
|
||||
+ "void main() {"
|
||||
+ " gl_FragColor = texture2D(tex, tex_c.xy);"
|
||||
+ "}";
|
||||
|
||||
// final static String lineVertexZigZagShader = ""
|
||||
// + "precision mediump float;"
|
||||
// + "uniform mat4 mvp;"
|
||||
// + "attribute vec4 a_pos1;"
|
||||
// + "attribute vec2 a_st1;"
|
||||
// + "attribute vec4 a_pos2;"
|
||||
// + "attribute vec2 a_st2;"
|
||||
// + "varying vec2 v_st;"
|
||||
// + "uniform vec2 u_mode;"
|
||||
// + "const float dscale = 1.0/1000.0;"
|
||||
// + "void main() {"
|
||||
// + "if (gl_VertexID & 1 == 0) {"
|
||||
// + " vec2 dir = dscale * u_mode[1] * a_pos1.zw;"
|
||||
// + " gl_Position = mvp * vec4(a_pos1.xy + dir, 0.0,1.0);"
|
||||
// + " v_st = u_mode[1] * a_st1;"
|
||||
// + "} else {"
|
||||
// + " vec2 dir = dscale * u_mode[1] * a_pos2.zw;"
|
||||
// + " gl_Position = mvp * vec4( a_pos1.xy, dir, 0.0,1.0);"
|
||||
// + " v_st = u_mode[1] * vec2(-a_st2.s , a_st2.t);"
|
||||
// + "}";
|
||||
|
||||
// final static String lineFragmentShader = ""
|
||||
// + "#extension GL_OES_standard_derivatives : enable\n"
|
||||
// + "precision mediump float;"
|
||||
// + "uniform vec2 u_mode;"
|
||||
// + "uniform vec4 u_color;"
|
||||
// + "varying vec2 v_st;"
|
||||
// + "const float zero = 0.0;"
|
||||
// + "void main() {"
|
||||
// + " vec4 color = u_color;"
|
||||
// + " float width = u_mode[1];"
|
||||
// + " float len;"
|
||||
// + " if (v_st.t == zero)"
|
||||
// + " len = abs(v_st.s);"
|
||||
// + " else "
|
||||
// + " len = length(v_st);"
|
||||
// + " vec2 st_width = fwidth(v_st);"
|
||||
// + " float fuzz = max(st_width.s, st_width.t);"
|
||||
// + " color.a *= smoothstep(-pixel, fuzz*pixel, width - (len * width));"
|
||||
// + " gl_FragColor = color;"
|
||||
// + "}";
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* 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.renderer;
|
||||
|
||||
public class SymbolLayer {
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* 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.renderer;
|
||||
|
||||
import org.oscim.theme.renderinstruction.Caption;
|
||||
import org.oscim.theme.renderinstruction.PathText;
|
||||
|
||||
public class TextItem {
|
||||
TextItem next;
|
||||
|
||||
final float x, y;
|
||||
final String text;
|
||||
final Caption caption;
|
||||
final PathText path;
|
||||
final float width;
|
||||
|
||||
short x1, y1, x2, y2;
|
||||
|
||||
public TextItem(float x, float y, String text, Caption caption) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.text = text;
|
||||
this.caption = caption;
|
||||
this.width = caption.paint.measureText(text);
|
||||
this.path = null;
|
||||
}
|
||||
|
||||
public TextItem(float x, float y, String text, PathText pathText, float width) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.text = text;
|
||||
this.path = pathText;
|
||||
this.caption = null;
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,474 +0,0 @@
|
||||
/*
|
||||
* 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.renderer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import org.oscim.utils.GlUtils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLUtils;
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
public class TextRenderer {
|
||||
private final static int TEXTURE_WIDTH = 512;
|
||||
private final static int TEXTURE_HEIGHT = 256;
|
||||
private final static float SCALE = 8.0f;
|
||||
private final static int LBIT_MASK = 0xfffffffe;
|
||||
// private final static int L2BIT_MASK = 0xfffffffc;
|
||||
|
||||
final static int INDICES_PER_SPRITE = 6;
|
||||
final static int VERTICES_PER_SPRITE = 4;
|
||||
final static int SHORTS_PER_VERTICE = 6;
|
||||
final static int MAX_LABELS = 35;
|
||||
|
||||
private static Bitmap mBitmap;
|
||||
private static Canvas mCanvas;
|
||||
private static int mFontPadX = 1;
|
||||
private static int mFontPadY = 1;
|
||||
private static int mBitmapFormat;
|
||||
private static int mBitmapType;
|
||||
private static ShortBuffer mShortBuffer;
|
||||
private static TextTexture[] mTextures;
|
||||
|
||||
private static int mIndicesVBO;
|
||||
private static int mVerticesVBO;
|
||||
|
||||
private static int mTextProgram;
|
||||
private static int hTextMVMatrix;
|
||||
private static int hTextProjectionMatrix;
|
||||
private static int hTextVertex;
|
||||
private static int hTextScale;
|
||||
private static int hTextTextureCoord;
|
||||
|
||||
private static Paint mPaint = new Paint(Color.BLACK);
|
||||
|
||||
private static boolean debug = false;
|
||||
private static short[] debugVertices = {
|
||||
|
||||
0, 0,
|
||||
0, TEXTURE_HEIGHT * 4,
|
||||
|
||||
0, TEXTURE_HEIGHT - 1,
|
||||
0, 0,
|
||||
|
||||
TEXTURE_WIDTH - 1, 0,
|
||||
TEXTURE_WIDTH * 4, TEXTURE_HEIGHT * 4,
|
||||
|
||||
TEXTURE_WIDTH - 1, TEXTURE_HEIGHT - 1,
|
||||
TEXTURE_WIDTH * 4, 0,
|
||||
|
||||
};
|
||||
|
||||
static void init() {
|
||||
mTextProgram = GlUtils.createProgram(Shaders.textVertexShader,
|
||||
Shaders.textFragmentShader);
|
||||
|
||||
hTextMVMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_mv");
|
||||
hTextProjectionMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_proj");
|
||||
hTextScale = GLES20.glGetUniformLocation(mTextProgram, "u_scale");
|
||||
hTextVertex = GLES20.glGetAttribLocation(mTextProgram, "vertex");
|
||||
hTextTextureCoord = GLES20.glGetAttribLocation(mTextProgram, "tex_coord");
|
||||
|
||||
}
|
||||
|
||||
static boolean setup(int numTextures) {
|
||||
int bufferSize = numTextures
|
||||
* MAX_LABELS * VERTICES_PER_SPRITE
|
||||
* SHORTS_PER_VERTICE * (Short.SIZE / 8);
|
||||
|
||||
// if (mBitmap == null) {
|
||||
mBitmap = Bitmap.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
mCanvas = new Canvas(mBitmap);
|
||||
|
||||
mBitmapFormat = GLUtils.getInternalFormat(mBitmap);
|
||||
mBitmapType = GLUtils.getType(mBitmap);
|
||||
|
||||
ByteBuffer buf = ByteBuffer.allocateDirect(bufferSize)
|
||||
.order(ByteOrder.nativeOrder());
|
||||
|
||||
mShortBuffer = buf.asShortBuffer();
|
||||
// }
|
||||
|
||||
int[] textureIds = new int[numTextures];
|
||||
TextTexture[] textures = new TextTexture[numTextures];
|
||||
GLES20.glGenTextures(numTextures, textureIds, 0);
|
||||
|
||||
for (int i = 0; i < numTextures; i++) {
|
||||
// setup filters for texture
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[i]);
|
||||
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
|
||||
GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
|
||||
GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
|
||||
GLES20.GL_CLAMP_TO_EDGE); // Set U Wrapping
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
|
||||
GLES20.GL_CLAMP_TO_EDGE); // Set V Wrapping
|
||||
|
||||
// load the generated bitmap onto the texture
|
||||
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmapFormat, mBitmap,
|
||||
mBitmapType, 0);
|
||||
|
||||
textures[i] = new TextTexture(textureIds[i]);
|
||||
}
|
||||
|
||||
GlUtils.checkGlError("init textures");
|
||||
|
||||
mTextures = textures;
|
||||
|
||||
// Setup triangle indices
|
||||
short[] indices = new short[MAX_LABELS * INDICES_PER_SPRITE];
|
||||
int len = indices.length;
|
||||
short j = 0;
|
||||
for (int i = 0; i < len; i += INDICES_PER_SPRITE, j += VERTICES_PER_SPRITE) {
|
||||
indices[i + 0] = (short) (j + 0);
|
||||
indices[i + 1] = (short) (j + 1);
|
||||
indices[i + 2] = (short) (j + 2);
|
||||
indices[i + 3] = (short) (j + 2);
|
||||
indices[i + 4] = (short) (j + 3);
|
||||
indices[i + 5] = (short) (j + 0);
|
||||
}
|
||||
|
||||
mShortBuffer.clear();
|
||||
mShortBuffer.put(indices, 0, len);
|
||||
mShortBuffer.flip();
|
||||
|
||||
int[] mVboIds = new int[2];
|
||||
GLES20.glGenBuffers(2, mVboIds, 0);
|
||||
mIndicesVBO = mVboIds[0];
|
||||
mVerticesVBO = mVboIds[1];
|
||||
|
||||
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
|
||||
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, len * (Short.SIZE / 8),
|
||||
mShortBuffer, GLES20.GL_STATIC_DRAW);
|
||||
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
mShortBuffer.clear();
|
||||
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
|
||||
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, bufferSize,
|
||||
mShortBuffer, GLES20.GL_DYNAMIC_DRAW);
|
||||
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean drawToTexture(MapTile tile) {
|
||||
TextTexture tex = null;
|
||||
|
||||
if (tile.labels == null)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < mTextures.length; i++) {
|
||||
tex = mTextures[i];
|
||||
if (tex.tile == null)
|
||||
break;
|
||||
|
||||
if (!tex.tile.isLocked())
|
||||
break;
|
||||
|
||||
tex = null;
|
||||
}
|
||||
|
||||
if (tex == null) {
|
||||
for (int i = 0; i < mTextures.length; i++) {
|
||||
tex = mTextures[i];
|
||||
if (!tex.tile.isVisible)
|
||||
break;
|
||||
|
||||
tex = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (tex == null) {
|
||||
Log.d(TAG, "no textures left");
|
||||
return false;
|
||||
}
|
||||
if (tex.tile != null)
|
||||
tex.tile.texture = null;
|
||||
|
||||
mBitmap.eraseColor(Color.TRANSPARENT);
|
||||
|
||||
int pos = 0;
|
||||
short[] buf = tex.vertices;
|
||||
|
||||
float y = 0;
|
||||
float x = mFontPadX;
|
||||
float width, height;
|
||||
|
||||
int max = MAX_LABELS;
|
||||
|
||||
if (debug) {
|
||||
mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[4],
|
||||
debugVertices[5], mPaint);
|
||||
mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[8],
|
||||
debugVertices[9], mPaint);
|
||||
|
||||
mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[4],
|
||||
debugVertices[5], mPaint);
|
||||
mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[8],
|
||||
debugVertices[9], mPaint);
|
||||
}
|
||||
|
||||
int advanceY = 0;
|
||||
|
||||
TextItem t = tile.labels;
|
||||
float yy;
|
||||
short x1, x2, x3, x4, y1, y2, y3, y4;
|
||||
|
||||
for (int i = 0; t != null && i < max; t = t.next, i++) {
|
||||
|
||||
if (t.caption != null) {
|
||||
height = (int) (t.caption.fontHeight) + 2 * mFontPadY;
|
||||
} else {
|
||||
height = (int) (t.path.fontHeight) + 2 * mFontPadY;
|
||||
}
|
||||
|
||||
width = t.width + 2 * mFontPadX;
|
||||
|
||||
if (height > advanceY)
|
||||
advanceY = (int) height;
|
||||
|
||||
if (x + width > TEXTURE_WIDTH) {
|
||||
x = mFontPadX;
|
||||
y += advanceY;
|
||||
advanceY = (int) height;
|
||||
}
|
||||
|
||||
if (t.caption != null) {
|
||||
yy = y + (height - 1) - t.caption.fontDescent - mFontPadY;
|
||||
} else {
|
||||
yy = y + (height - 1) - t.path.fontDescent - mFontPadY;
|
||||
}
|
||||
|
||||
if (yy > TEXTURE_HEIGHT) {
|
||||
Log.d(TAG, "reached max labels");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t.caption != null) {
|
||||
if (t.caption.stroke != null)
|
||||
mCanvas.drawText(t.text, x + t.width / 2, yy, t.caption.stroke);
|
||||
|
||||
mCanvas.drawText(t.text, x + t.width / 2, yy, t.caption.paint);
|
||||
} else {
|
||||
if (t.path.stroke != null)
|
||||
mCanvas.drawText(t.text, x + t.width / 2, yy, t.path.stroke);
|
||||
|
||||
mCanvas.drawText(t.text, x + t.width / 2, yy, t.path.paint);
|
||||
}
|
||||
if (width > TEXTURE_WIDTH)
|
||||
width = TEXTURE_WIDTH;
|
||||
|
||||
float hw = width / 2.0f;
|
||||
float hh = height / 2.0f;
|
||||
|
||||
if (t.caption != null) {
|
||||
x1 = x3 = (short) (SCALE * (-hw));
|
||||
y1 = y3 = (short) (SCALE * (hh));
|
||||
x2 = x4 = (short) (SCALE * (hw));
|
||||
y2 = y4 = (short) (SCALE * (-hh));
|
||||
} else {
|
||||
float vx = t.x1 - t.x2;
|
||||
float vy = t.y1 - t.y2;
|
||||
float a = FloatMath.sqrt(vx * vx + vy * vy);
|
||||
vx = vx / a;
|
||||
vy = vy / a;
|
||||
|
||||
float ux = -vy;
|
||||
float uy = vx;
|
||||
|
||||
// int dx = (int) (vx * SCALE) & L2BIT_MASK;
|
||||
// int dy = (int) (vy * SCALE) & L2BIT_MASK;
|
||||
//
|
||||
// x1 = (short) dx;
|
||||
// y1 = (short) dy;
|
||||
//
|
||||
// x2 = (short) (dx | 1);
|
||||
// y3 = (short) (dy | 1);
|
||||
//
|
||||
// x4 = (short) (dx | 3);
|
||||
// y4 = (short) (dy | 3);
|
||||
//
|
||||
// x3 = (short) (dx | 2);
|
||||
// y2 = (short) (dy | 2);
|
||||
|
||||
x1 = (short) (SCALE * (vx * hw - ux * hh));
|
||||
y1 = (short) (SCALE * (vy * hw - uy * hh));
|
||||
x2 = (short) (SCALE * (-vx * hw - ux * hh));
|
||||
y3 = (short) (SCALE * (-vy * hw - uy * hh));
|
||||
x4 = (short) (SCALE * (-vx * hw + ux * hh));
|
||||
y4 = (short) (SCALE * (-vy * hw + uy * hh));
|
||||
x3 = (short) (SCALE * (vx * hw + ux * hh));
|
||||
y2 = (short) (SCALE * (vy * hw + uy * hh));
|
||||
|
||||
}
|
||||
short u1 = (short) (SCALE * x);
|
||||
short v1 = (short) (SCALE * y);
|
||||
short u2 = (short) (SCALE * (x + width));
|
||||
short v2 = (short) (SCALE * (y + height));
|
||||
|
||||
// pack caption/way-text info in lowest bit
|
||||
short tx;
|
||||
if (t.caption == null)
|
||||
tx = (short) ((int) (SCALE * t.x) & LBIT_MASK | 0);
|
||||
else
|
||||
tx = (short) ((int) (SCALE * t.x) & LBIT_MASK | 1);
|
||||
|
||||
short ty = (short) (SCALE * t.y);
|
||||
|
||||
// top-left
|
||||
buf[pos++] = tx;
|
||||
buf[pos++] = ty;
|
||||
buf[pos++] = x1;
|
||||
buf[pos++] = y1;
|
||||
buf[pos++] = u1;
|
||||
buf[pos++] = v2;
|
||||
|
||||
// top-right
|
||||
buf[pos++] = tx;
|
||||
buf[pos++] = ty;
|
||||
buf[pos++] = x2;
|
||||
buf[pos++] = y3;
|
||||
buf[pos++] = u2;
|
||||
buf[pos++] = v2;
|
||||
|
||||
// bot-right
|
||||
buf[pos++] = tx;
|
||||
buf[pos++] = ty;
|
||||
buf[pos++] = x4;
|
||||
buf[pos++] = y4;
|
||||
buf[pos++] = u2;
|
||||
buf[pos++] = v1;
|
||||
|
||||
// bot-left
|
||||
buf[pos++] = tx;
|
||||
buf[pos++] = ty;
|
||||
buf[pos++] = x3;
|
||||
buf[pos++] = y2;
|
||||
buf[pos++] = u1;
|
||||
buf[pos++] = v1;
|
||||
|
||||
x += width;
|
||||
|
||||
if (y > TEXTURE_HEIGHT) {
|
||||
Log.d(TAG, "reached max labels: texture is full");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tex.length = pos;
|
||||
tile.texture = tex;
|
||||
tex.tile = tile;
|
||||
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.id);
|
||||
GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mBitmap,
|
||||
mBitmapFormat, mBitmapType);
|
||||
|
||||
// FIXME shouldnt be needed here, still looking for sometimes corrupted
|
||||
// labels..
|
||||
GLES20.glFlush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String TAG = "TextRenderer";
|
||||
|
||||
static void compileTextures() {
|
||||
int offset = 0;
|
||||
TextTexture tex;
|
||||
|
||||
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
|
||||
|
||||
mShortBuffer.clear();
|
||||
|
||||
for (int i = 0; i < mTextures.length; i++) {
|
||||
tex = mTextures[i];
|
||||
if (tex.tile == null) // || !tex.tile.isLocked)
|
||||
continue;
|
||||
|
||||
mShortBuffer.put(tex.vertices, 0, tex.length);
|
||||
tex.offset = offset;
|
||||
offset += tex.length;
|
||||
}
|
||||
|
||||
mShortBuffer.flip();
|
||||
|
||||
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, offset * (Short.SIZE / 8),
|
||||
mShortBuffer);
|
||||
}
|
||||
|
||||
static void beginDraw(float scale, float[] projection) {
|
||||
GLES20.glUseProgram(mTextProgram);
|
||||
|
||||
GLES20.glEnableVertexAttribArray(hTextTextureCoord);
|
||||
GLES20.glEnableVertexAttribArray(hTextVertex);
|
||||
|
||||
GLES20.glUniform1f(hTextScale, scale);
|
||||
GLES20.glUniformMatrix4fv(hTextProjectionMatrix, 1, false, projection, 0);
|
||||
|
||||
if (debug) {
|
||||
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
|
||||
mShortBuffer.clear();
|
||||
mShortBuffer.put(debugVertices, 0, 16);
|
||||
mShortBuffer.flip();
|
||||
GLES20.glVertexAttribPointer(hTextVertex, 2,
|
||||
GLES20.GL_SHORT, false, 8, mShortBuffer);
|
||||
mShortBuffer.position(2);
|
||||
GLES20.glVertexAttribPointer(hTextTextureCoord, 2,
|
||||
GLES20.GL_SHORT, false, 8, mShortBuffer);
|
||||
} else {
|
||||
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
|
||||
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
|
||||
}
|
||||
}
|
||||
|
||||
static void endDraw() {
|
||||
|
||||
GLES20.glDisableVertexAttribArray(hTextTextureCoord);
|
||||
GLES20.glDisableVertexAttribArray(hTextVertex);
|
||||
}
|
||||
|
||||
static void drawTile(MapTile tile, float[] matrix) {
|
||||
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tile.texture.id);
|
||||
|
||||
GLES20.glUniformMatrix4fv(hTextMVMatrix, 1, false, matrix, 0);
|
||||
|
||||
if (debug) {
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
} else {
|
||||
|
||||
GLES20.glVertexAttribPointer(hTextVertex, 4,
|
||||
GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8));
|
||||
|
||||
GLES20.glVertexAttribPointer(hTextTextureCoord, 2,
|
||||
GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8)
|
||||
+ 8);
|
||||
|
||||
GLES20.glDrawElements(GLES20.GL_TRIANGLES, (tile.texture.length / 24) *
|
||||
INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* 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.renderer;
|
||||
|
||||
public class TextTexture {
|
||||
|
||||
final short[] vertices;
|
||||
final int id;
|
||||
int length;
|
||||
int offset;
|
||||
MapTile tile;
|
||||
|
||||
String[] text;
|
||||
|
||||
TextTexture(int textureID) {
|
||||
vertices = new short[TextRenderer.MAX_LABELS *
|
||||
TextRenderer.VERTICES_PER_SPRITE *
|
||||
TextRenderer.SHORTS_PER_VERTICE];
|
||||
id = textureID;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* 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.renderer;
|
||||
|
||||
public class TextureLayer {
|
||||
|
||||
}
|
||||
@@ -1,616 +0,0 @@
|
||||
/*
|
||||
* 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.renderer;
|
||||
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.core.Tag;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.core.WebMercator;
|
||||
import org.oscim.database.IMapDatabase;
|
||||
import org.oscim.database.IMapDatabaseCallback;
|
||||
import org.oscim.database.QueryResult;
|
||||
import org.oscim.theme.IRenderCallback;
|
||||
import org.oscim.theme.RenderTheme;
|
||||
import org.oscim.theme.renderinstruction.Area;
|
||||
import org.oscim.theme.renderinstruction.Caption;
|
||||
import org.oscim.theme.renderinstruction.Line;
|
||||
import org.oscim.theme.renderinstruction.PathText;
|
||||
import org.oscim.theme.renderinstruction.RenderInstruction;
|
||||
import org.oscim.view.DebugSettings;
|
||||
import org.oscim.view.MapView;
|
||||
import org.oscim.view.generator.JobTile;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Paint;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
|
||||
|
||||
private static String TAG = TileGenerator.class.getName();
|
||||
|
||||
private static final double PI180 = (Math.PI / 180) / 1000000.0;
|
||||
private static final double PIx4 = Math.PI * 4;
|
||||
private static final double STROKE_INCREASE = Math.sqrt(2);
|
||||
private static final byte LAYERS = 11;
|
||||
private static final double f900913 = 20037508.342789244;
|
||||
|
||||
static final byte STROKE_MIN_ZOOM_LEVEL = 12;
|
||||
static final byte STROKE_MAX_ZOOM_LEVEL = 17;
|
||||
|
||||
private static RenderTheme renderTheme;
|
||||
|
||||
private IMapDatabase mMapDatabase;
|
||||
|
||||
private MapTile mCurrentTile;
|
||||
|
||||
private float[] mCoords;
|
||||
private short[] mIndices;
|
||||
|
||||
private LineLayer mLineLayers;
|
||||
private PolygonLayer mPolyLayers;
|
||||
private LineLayer mCurLineLayer;
|
||||
private PolygonLayer mCurPolyLayer;
|
||||
|
||||
private TextItem mLabels;
|
||||
|
||||
private int mDrawingLayer;
|
||||
private int mLevels;
|
||||
|
||||
private float mStrokeScale = 1.0f;
|
||||
|
||||
private boolean mProjected;
|
||||
// private boolean mProjectedResult;
|
||||
private float mSimplify;
|
||||
// private boolean firstMatch;
|
||||
// private boolean prevClosed;
|
||||
|
||||
private RenderInstruction[] mRenderInstructions = null;
|
||||
|
||||
private final String TAG_WATER = "water".intern();
|
||||
private final MapView mMapView;
|
||||
|
||||
private final Tag[] debugTagBox = { new Tag("debug", "box") };
|
||||
private final Tag[] debugTagWay = { new Tag("debug", "way") };
|
||||
private final Tag[] debugTagArea = { new Tag("debug", "area") };
|
||||
private final float[] debugBoxCoords = { 0, 0, 0, Tile.TILE_SIZE,
|
||||
Tile.TILE_SIZE, Tile.TILE_SIZE, Tile.TILE_SIZE, 0, 0, 0 };
|
||||
private final short[] debugBoxIndex = { 10 };
|
||||
|
||||
private float mProjectionScaleFactor;
|
||||
|
||||
/**
|
||||
* @param mapView
|
||||
* the MapView
|
||||
*/
|
||||
public TileGenerator(MapView mapView) {
|
||||
Log.d(TAG, "init TileGenerator");
|
||||
mMapView = mapView;
|
||||
}
|
||||
|
||||
private float mPoiX = 256;
|
||||
private float mPoiY = 256;
|
||||
|
||||
private Tag mTagEmptyName = new Tag(Tag.TAG_KEY_NAME, null, false);
|
||||
private Tag mTagName;
|
||||
|
||||
private void filterTags(Tag[] tags) {
|
||||
for (int i = 0; i < tags.length; i++) {
|
||||
// Log.d(TAG, "check tag: " + tags[i]);
|
||||
if (tags[i].key == Tag.TAG_KEY_NAME) {
|
||||
mTagName = tags[i];
|
||||
tags[i] = mTagEmptyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private RenderInstruction[] mNodeRenderInstructions;
|
||||
|
||||
@Override
|
||||
public void renderPointOfInterest(byte layer, Tag[] tags, float latitude,
|
||||
float longitude) {
|
||||
|
||||
mTagName = null;
|
||||
|
||||
if (mMapProjection != null)
|
||||
{
|
||||
long x = mCurrentTile.pixelX;
|
||||
long y = mCurrentTile.pixelY + Tile.TILE_SIZE;
|
||||
long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel;
|
||||
|
||||
double divx, divy;
|
||||
long dx = (x - (z >> 1));
|
||||
long dy = (y - (z >> 1));
|
||||
|
||||
if (mMapProjection == WebMercator.NAME) {
|
||||
double div = f900913 / (z >> 1);
|
||||
// divy = f900913 / (z >> 1);
|
||||
mPoiX = (float) (longitude / div - dx);
|
||||
mPoiY = (float) (latitude / div + dy);
|
||||
} else {
|
||||
divx = 180000000.0 / (z >> 1);
|
||||
divy = z / PIx4;
|
||||
mPoiX = (float) (longitude / divx - dx);
|
||||
double sinLat = Math.sin(latitude * PI180);
|
||||
mPoiY = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy);
|
||||
if (mPoiX < -10 || mPoiX > Tile.TILE_SIZE + 10 || mPoiY < -10
|
||||
|| mPoiY > Tile.TILE_SIZE + 10)
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mPoiX = longitude;
|
||||
mPoiY = latitude;
|
||||
}
|
||||
|
||||
// remove tags that should not be cached in Rendertheme
|
||||
filterTags(tags);
|
||||
// Log.d(TAG, "renderPointOfInterest: " + mTagName);
|
||||
|
||||
// mNodeRenderInstructions =
|
||||
TileGenerator.renderTheme.matchNode(this, tags, mCurrentTile.zoomLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWaterBackground() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
private boolean mClosed;
|
||||
|
||||
@Override
|
||||
public void renderWay(byte layer, Tag[] tags, float[] coords, short[] indices,
|
||||
boolean closed) {
|
||||
|
||||
mTagName = null;
|
||||
mProjected = false;
|
||||
mCurLineLayer = null;
|
||||
mClosed = closed;
|
||||
|
||||
mDrawingLayer = getValidLayer(layer) * mLevels;
|
||||
mSimplify = 0.5f;
|
||||
|
||||
if (closed) {
|
||||
if (mCurrentTile.zoomLevel < 14)
|
||||
mSimplify = 0.5f;
|
||||
else
|
||||
mSimplify = 0.2f;
|
||||
|
||||
if (tags.length == 1 && TAG_WATER == (tags[0].value))
|
||||
mSimplify = 0;
|
||||
}
|
||||
|
||||
mCoords = coords;
|
||||
mIndices = indices;
|
||||
|
||||
// remove tags that should not be cached in Rendertheme
|
||||
filterTags(tags);
|
||||
|
||||
// if (mRenderInstructions != null) {
|
||||
// for (int i = 0, n = mRenderInstructions.length; i < n; i++)
|
||||
// mRenderInstructions[i].renderWay(this, tags);
|
||||
// }
|
||||
|
||||
mRenderInstructions = TileGenerator.renderTheme.matchWay(this, tags,
|
||||
(byte) (mCurrentTile.zoomLevel + 0),
|
||||
closed, true);
|
||||
|
||||
if (mRenderInstructions == null && mDebugDrawUnmatched)
|
||||
debugUnmatched(closed, tags);
|
||||
}
|
||||
|
||||
private void debugUnmatched(boolean closed, Tag[] tags) {
|
||||
|
||||
Log.d(TAG, "way not matched: " + tags[0] + " "
|
||||
+ (tags.length > 1 ? tags[1] : "") + " " + closed);
|
||||
|
||||
mTagName = new Tag("name", tags[0].key + ":" + tags[0].value, false);
|
||||
|
||||
if (closed) {
|
||||
mRenderInstructions = TileGenerator.renderTheme.matchWay(this, debugTagArea,
|
||||
(byte) 0, true, true);
|
||||
} else {
|
||||
mRenderInstructions = TileGenerator.renderTheme.matchWay(this, debugTagWay,
|
||||
(byte) 0, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderAreaCaption(Caption caption) {
|
||||
// Log.d(TAG, "renderAreaCaption: " + mTagName);
|
||||
|
||||
if (mTagName == null)
|
||||
return;
|
||||
|
||||
if (caption.textKey == mTagEmptyName.key) {
|
||||
|
||||
TextItem t = new TextItem(mCoords[0], mCoords[1], mTagName.value, caption);
|
||||
t.next = mLabels;
|
||||
mLabels = t;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderPointOfInterestCaption(Caption caption) {
|
||||
// Log.d(TAG, "renderPointOfInterestCaption: " + mPoiX + " " + mPoiY +
|
||||
// " " + mTagName);
|
||||
|
||||
if (mTagName == null)
|
||||
return;
|
||||
|
||||
if (caption.textKey == mTagEmptyName.key) {
|
||||
TextItem t = new TextItem(mPoiX, mPoiY, mTagName.value, caption);
|
||||
t.next = mLabels;
|
||||
mLabels = t;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWayText(PathText pathText) {
|
||||
// Log.d(TAG, "renderWayText: " + mTagName);
|
||||
|
||||
if (mTagName == null)
|
||||
return;
|
||||
|
||||
if (pathText.textKey == mTagEmptyName.key && mTagName.value != null) {
|
||||
|
||||
mLabels = WayDecorator.renderText(mCoords, mTagName.value, pathText, 0,
|
||||
mIndices[0], mLabels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderAreaSymbol(Bitmap symbol) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderPointOfInterestCircle(float radius, Paint fill, int level) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderPointOfInterestSymbol(Bitmap symbol) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
private int countLines;
|
||||
private int countNodes;
|
||||
|
||||
@Override
|
||||
public void renderWay(Line line, int level) {
|
||||
|
||||
projectToTile();
|
||||
|
||||
if (line.outline && mCurLineLayer == null)
|
||||
return;
|
||||
|
||||
float w = line.width;
|
||||
|
||||
if (!line.fixed) {
|
||||
w *= mStrokeScale;
|
||||
w *= mProjectionScaleFactor;
|
||||
}
|
||||
|
||||
LineLayer lineLayer = null;
|
||||
|
||||
int numLayer = mDrawingLayer + level;
|
||||
|
||||
LineLayer l = mLineLayers;
|
||||
|
||||
if (mCurLineLayer != null && mCurLineLayer.layer == numLayer) {
|
||||
lineLayer = mCurLineLayer;
|
||||
} else if (l == null || l.layer > numLayer) {
|
||||
// insert new layer at start
|
||||
lineLayer = new LineLayer(numLayer, line, w, line.outline);
|
||||
// lineLayer = LineLayers.get(numLayer, line, w, false);
|
||||
|
||||
lineLayer.next = l;
|
||||
mLineLayers = lineLayer;
|
||||
} else {
|
||||
while (l != null) {
|
||||
// found layer
|
||||
if (l.layer == numLayer) {
|
||||
lineLayer = l;
|
||||
break;
|
||||
}
|
||||
// insert new layer between current and next layer
|
||||
if (l.next == null || l.next.layer > numLayer) {
|
||||
lineLayer = new LineLayer(numLayer, line, w, line.outline);
|
||||
// lineLayer = LineLayers.get(numLayer, line, w, false);
|
||||
lineLayer.next = l.next;
|
||||
l.next = lineLayer;
|
||||
break;
|
||||
}
|
||||
l = l.next;
|
||||
}
|
||||
}
|
||||
|
||||
if (lineLayer == null) {
|
||||
mCurLineLayer = null;
|
||||
return;
|
||||
}
|
||||
if (line.outline) {
|
||||
lineLayer.addOutline(mCurLineLayer);
|
||||
return;
|
||||
}
|
||||
|
||||
mCurLineLayer = lineLayer;
|
||||
|
||||
lineLayer.addLine(mCoords, mIndices, mClosed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderArea(Area area, int level) {
|
||||
if (!mDebugDrawPolygons)
|
||||
return;
|
||||
|
||||
if (!mProjected && !projectToTile())
|
||||
return;
|
||||
|
||||
int numLayer = mDrawingLayer + level;
|
||||
|
||||
PolygonLayer layer = null;
|
||||
PolygonLayer l = mPolyLayers;
|
||||
|
||||
if (mCurPolyLayer != null && mCurPolyLayer.layer == numLayer) {
|
||||
layer = mCurPolyLayer;
|
||||
} else if (l == null || l.layer > numLayer) {
|
||||
// insert new layer at start
|
||||
layer = new PolygonLayer(numLayer, area);
|
||||
layer.next = l;
|
||||
mPolyLayers = layer;
|
||||
} else {
|
||||
while (l != null) {
|
||||
|
||||
if (l.layer == numLayer) {
|
||||
layer = l;
|
||||
break;
|
||||
}
|
||||
|
||||
// insert new layer between current and next layer
|
||||
if (l.next == null || l.next.layer > numLayer) {
|
||||
layer = new PolygonLayer(numLayer, area);
|
||||
layer.next = l.next;
|
||||
l.next = layer;
|
||||
break;
|
||||
}
|
||||
l = l.next;
|
||||
}
|
||||
}
|
||||
if (layer == null)
|
||||
return;
|
||||
|
||||
mCurPolyLayer = layer;
|
||||
|
||||
layer.addPolygon(mCoords, mIndices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWaySymbol(Bitmap symbol, boolean alignCenter, boolean repeat) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
private boolean mDebugDrawPolygons;
|
||||
boolean mDebugDrawUnmatched;
|
||||
|
||||
public boolean executeJob(JobTile jobTile) {
|
||||
MapTile tile;
|
||||
|
||||
if (mMapDatabase == null)
|
||||
return false;
|
||||
|
||||
tile = mCurrentTile = (MapTile) jobTile;
|
||||
DebugSettings debugSettings = mMapView.getDebugSettings();
|
||||
|
||||
mDebugDrawPolygons = !debugSettings.mDisablePolygons;
|
||||
mDebugDrawUnmatched = debugSettings.mDrawUnmatchted;
|
||||
|
||||
if (tile.newData || tile.isReady) {
|
||||
// fixed now....
|
||||
Log.d(TAG, "XXX tile already loaded "
|
||||
+ tile + " "
|
||||
+ tile.newData + " "
|
||||
+ tile.isReady + " ");
|
||||
return false;
|
||||
}
|
||||
|
||||
mLevels = TileGenerator.renderTheme.getLevels();
|
||||
|
||||
// limit stroke scale at z=17
|
||||
if (tile.zoomLevel < STROKE_MAX_ZOOM_LEVEL)
|
||||
setScaleStrokeWidth(tile.zoomLevel);
|
||||
else
|
||||
setScaleStrokeWidth(STROKE_MAX_ZOOM_LEVEL);
|
||||
|
||||
// firstMatch = true;
|
||||
countLines = 0;
|
||||
countNodes = 0;
|
||||
|
||||
// acount for area changes with latitude
|
||||
mProjectionScaleFactor = 0.5f + (float) (0.5 / Math.cos(MercatorProjection
|
||||
.pixelYToLatitude(tile.pixelY, tile.zoomLevel)
|
||||
* (Math.PI / 180)));
|
||||
|
||||
if (mMapDatabase.executeQuery(tile, this) != QueryResult.SUCCESS) {
|
||||
Log.d(TAG, "Failed loading: " + tile);
|
||||
LineRenderer.clear(mLineLayers);
|
||||
PolygonRenderer.clear(mPolyLayers);
|
||||
mLineLayers = null;
|
||||
mPolyLayers = null;
|
||||
mLabels = null;
|
||||
tile.isLoading = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (debugSettings.mDrawTileFrames) {
|
||||
mTagName = new Tag("name", tile.toString(), false);
|
||||
mPoiX = Tile.TILE_SIZE >> 1;
|
||||
mPoiY = 10;
|
||||
TileGenerator.renderTheme.matchNode(this, debugTagWay, (byte) 0);
|
||||
|
||||
mIndices = debugBoxIndex;
|
||||
mCoords = debugBoxCoords;
|
||||
mDrawingLayer = 10 * mLevels;
|
||||
TileGenerator.renderTheme.matchWay(this, debugTagBox, (byte) 0, false, true);
|
||||
}
|
||||
|
||||
tile.lineLayers = mLineLayers;
|
||||
tile.polygonLayers = mPolyLayers;
|
||||
tile.labels = mLabels;
|
||||
|
||||
mCurPolyLayer = null;
|
||||
mCurLineLayer = null;
|
||||
mLineLayers = null;
|
||||
mPolyLayers = null;
|
||||
mLabels = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static byte getValidLayer(byte layer) {
|
||||
if (layer < 0) {
|
||||
return 0;
|
||||
/**
|
||||
* return instances of MapRenderer
|
||||
*/
|
||||
} else if (layer >= LAYERS) {
|
||||
return LAYERS - 1;
|
||||
} else {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scale stroke factor for the given zoom level.
|
||||
*
|
||||
* @param zoomLevel
|
||||
* the zoom level for which the scale stroke factor should be
|
||||
* set.
|
||||
*/
|
||||
private void setScaleStrokeWidth(byte zoomLevel) {
|
||||
int zoomLevelDiff = Math.max(zoomLevel - STROKE_MIN_ZOOM_LEVEL, 0);
|
||||
mStrokeScale = (float) Math.pow(STROKE_INCREASE, zoomLevelDiff);
|
||||
if (mStrokeScale < 1)
|
||||
mStrokeScale = 1;
|
||||
}
|
||||
|
||||
private String mMapProjection;
|
||||
|
||||
public void setMapDatabase(IMapDatabase mapDatabase) {
|
||||
mMapDatabase = mapDatabase;
|
||||
mMapProjection = mMapDatabase.getMapProjection();
|
||||
}
|
||||
|
||||
public IMapDatabase getMapDatabase() {
|
||||
return mMapDatabase;
|
||||
}
|
||||
|
||||
public void setRenderTheme(RenderTheme theme) {
|
||||
TileGenerator.renderTheme = theme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkWay(Tag[] tags, boolean closed) {
|
||||
|
||||
mRenderInstructions = TileGenerator.renderTheme.matchWay(this, tags,
|
||||
(byte) (mCurrentTile.zoomLevel + 0), closed, false);
|
||||
|
||||
return mRenderInstructions != null;
|
||||
}
|
||||
|
||||
private boolean projectToTile() {
|
||||
if (mProjected || mMapProjection == null)
|
||||
return true;
|
||||
|
||||
boolean useWebMercator = false;
|
||||
|
||||
if (mMapProjection == WebMercator.NAME)
|
||||
useWebMercator = true;
|
||||
|
||||
float[] coords = mCoords;
|
||||
|
||||
long x = mCurrentTile.pixelX;
|
||||
long y = mCurrentTile.pixelY + Tile.TILE_SIZE;
|
||||
long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel;
|
||||
float min = mSimplify;
|
||||
|
||||
double divx, divy;
|
||||
long dx = (x - (z >> 1));
|
||||
long dy = (y - (z >> 1));
|
||||
|
||||
if (useWebMercator) {
|
||||
divx = f900913 / (z >> 1);
|
||||
divy = f900913 / (z >> 1);
|
||||
} else {
|
||||
divx = 180000000.0 / (z >> 1);
|
||||
divy = z / PIx4;
|
||||
}
|
||||
|
||||
for (int pos = 0, outPos = 0, i = 0, m = mIndices.length; i < m; i++) {
|
||||
int len = mIndices[i];
|
||||
if (len == 0)
|
||||
continue;
|
||||
if (len < 0)
|
||||
break;
|
||||
|
||||
int cnt = 0;
|
||||
float lat, lon, prevLon = 0, prevLat = 0;
|
||||
|
||||
for (int end = pos + len; pos < end; pos += 2) {
|
||||
|
||||
if (useWebMercator) {
|
||||
lon = (float) (coords[pos] / divx - dx);
|
||||
lat = (float) (coords[pos + 1] / divy + dy);
|
||||
} else {
|
||||
lon = (float) ((coords[pos]) / divx - dx);
|
||||
double sinLat = Math.sin(coords[pos + 1] * PI180);
|
||||
lat = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy);
|
||||
}
|
||||
|
||||
if (cnt != 0) {
|
||||
// drop small distance intermediate nodes
|
||||
if (lat == prevLat && lon == prevLon)
|
||||
continue;
|
||||
|
||||
if ((pos != end - 2) &&
|
||||
!((lat > prevLat + min || lat < prevLat - min) ||
|
||||
(lon > prevLon + min || lon < prevLon - min)))
|
||||
continue;
|
||||
}
|
||||
coords[outPos++] = prevLon = lon;
|
||||
coords[outPos++] = prevLat = lat;
|
||||
|
||||
cnt += 2;
|
||||
}
|
||||
|
||||
mIndices[i] = (short) cnt;
|
||||
}
|
||||
mProjected = true;
|
||||
// mProjectedResult = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oscim.view.renderer;
|
||||
|
||||
class VertexBufferObject {
|
||||
int id;
|
||||
int size;
|
||||
|
||||
VertexBufferObject(int id) {
|
||||
this.id = id;
|
||||
size = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* 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.renderer;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class VertexPool {
|
||||
private static final int POOL_LIMIT = 6000;
|
||||
|
||||
static private VertexPoolItem pool = null;
|
||||
static private int count = 0;
|
||||
static private int countAll = 0;
|
||||
|
||||
static synchronized void init() {
|
||||
count = 0;
|
||||
countAll = 0;
|
||||
pool = null;
|
||||
}
|
||||
|
||||
static synchronized VertexPoolItem get() {
|
||||
|
||||
if (pool == null && count > 0) {
|
||||
Log.d("VertexPool", "XXX wrong count: " + count);
|
||||
}
|
||||
if (pool == null) {
|
||||
countAll++;
|
||||
return new VertexPoolItem();
|
||||
}
|
||||
|
||||
count--;
|
||||
|
||||
if (count < 0) {
|
||||
int c = 0;
|
||||
|
||||
for (VertexPoolItem tmp = pool; tmp != null; tmp = tmp.next)
|
||||
c++;
|
||||
|
||||
Log.d("VertexPool", "XXX wrong count: " + count + " left" + c);
|
||||
return new VertexPoolItem();
|
||||
}
|
||||
|
||||
VertexPoolItem it = pool;
|
||||
pool = pool.next;
|
||||
it.used = 0;
|
||||
it.next = null;
|
||||
return it;
|
||||
}
|
||||
|
||||
// private static float load = 1.0f;
|
||||
// private static int loadCount = 0;
|
||||
|
||||
static synchronized void add(VertexPoolItem items) {
|
||||
if (items == null)
|
||||
return;
|
||||
|
||||
// int pall = countAll;
|
||||
// int pcnt = count;
|
||||
|
||||
// limit pool items
|
||||
if (countAll < POOL_LIMIT) {
|
||||
|
||||
VertexPoolItem last = items;
|
||||
|
||||
while (true) {
|
||||
count++;
|
||||
// load += (float) last.used / VertexPoolItem.SIZE;
|
||||
// loadCount++;
|
||||
|
||||
if (last.next == null)
|
||||
break;
|
||||
|
||||
last = last.next;
|
||||
}
|
||||
|
||||
last.next = pool;
|
||||
pool = items;
|
||||
// Log.d("Pool", "added: " + (count - pcnt) + " " + count + " " + countAll
|
||||
// + " load: " + (load / loadCount));
|
||||
|
||||
} else {
|
||||
// int cleared = 0;
|
||||
VertexPoolItem prev, tmp = items;
|
||||
while (tmp != null) {
|
||||
prev = tmp;
|
||||
tmp = tmp.next;
|
||||
|
||||
countAll--;
|
||||
|
||||
// load += (float) prev.used / VertexPoolItem.SIZE;
|
||||
// loadCount++;
|
||||
|
||||
prev.next = null;
|
||||
|
||||
}
|
||||
// Log.d("Pool", "dropped: " + (pall - countAll) + " " + count + " "
|
||||
// + countAll + " load: " + (load / loadCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* 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.renderer;
|
||||
|
||||
public class VertexPoolItem {
|
||||
final short[] vertices;
|
||||
int used;
|
||||
VertexPoolItem next;
|
||||
|
||||
VertexPoolItem() {
|
||||
vertices = new short[SIZE];
|
||||
used = 0;
|
||||
}
|
||||
|
||||
// must be multiple of 4 (expected in LineLayer/PolygonLayer)
|
||||
static final int SIZE = 256;
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* 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.renderer;
|
||||
|
||||
import org.oscim.theme.renderinstruction.PathText;
|
||||
import org.oscim.utils.GeometryUtils;
|
||||
|
||||
import android.util.FloatMath;
|
||||
|
||||
final class WayDecorator {
|
||||
// /**
|
||||
// * Minimum distance in pixels before the symbol is repeated.
|
||||
// */
|
||||
// private static final int DISTANCE_BETWEEN_SYMBOLS = 200;
|
||||
|
||||
// /**
|
||||
// * Minimum distance in pixels before the way name is repeated.
|
||||
// */
|
||||
// private static final int DISTANCE_BETWEEN_WAY_NAMES = 500;
|
||||
|
||||
// /**
|
||||
// * Distance in pixels to skip from both ends of a segment.
|
||||
// */
|
||||
// private static final int SEGMENT_SAFETY_DISTANCE = 30;
|
||||
|
||||
// static void renderSymbol(Bitmap symbolBitmap, boolean alignCenter,
|
||||
// boolean repeatSymbol, float[][] coordinates,
|
||||
// List<SymbolContainer> waySymbols) {
|
||||
// int skipPixels = SEGMENT_SAFETY_DISTANCE;
|
||||
//
|
||||
// // get the first way point coordinates
|
||||
// float previousX = coordinates[0][0];
|
||||
// float previousY = coordinates[0][1];
|
||||
//
|
||||
// // draw the symbol on each way segment
|
||||
// float segmentLengthRemaining;
|
||||
// float segmentSkipPercentage;
|
||||
// float symbolAngle;
|
||||
// for (int i = 2; i < coordinates[0].length; i += 2) {
|
||||
// // get the current way point coordinates
|
||||
// float currentX = coordinates[0][i];
|
||||
// float currentY = coordinates[0][i + 1];
|
||||
//
|
||||
// // calculate the length of the current segment (Euclidian distance)
|
||||
// float diffX = currentX - previousX;
|
||||
// float diffY = currentY - previousY;
|
||||
// double segmentLengthInPixel = Math.sqrt(diffX * diffX + diffY * diffY);
|
||||
// segmentLengthRemaining = (float) segmentLengthInPixel;
|
||||
//
|
||||
// while (segmentLengthRemaining - skipPixels > SEGMENT_SAFETY_DISTANCE) {
|
||||
// // calculate the percentage of the current segment to skip
|
||||
// segmentSkipPercentage = skipPixels / segmentLengthRemaining;
|
||||
//
|
||||
// // move the previous point forward towards the current point
|
||||
// previousX += diffX * segmentSkipPercentage;
|
||||
// previousY += diffY * segmentSkipPercentage;
|
||||
// symbolAngle = (float) Math.toDegrees(Math.atan2(currentY - previousY,
|
||||
// currentX - previousX));
|
||||
//
|
||||
// waySymbols.add(new SymbolContainer(symbolBitmap, previousX, previousY,
|
||||
// alignCenter, symbolAngle));
|
||||
//
|
||||
// // check if the symbol should only be rendered once
|
||||
// if (!repeatSymbol) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // recalculate the distances
|
||||
// diffX = currentX - previousX;
|
||||
// diffY = currentY - previousY;
|
||||
//
|
||||
// // recalculate the remaining length of the current segment
|
||||
// segmentLengthRemaining -= skipPixels;
|
||||
//
|
||||
// // set the amount of pixels to skip before repeating the symbol
|
||||
// skipPixels = DISTANCE_BETWEEN_SYMBOLS;
|
||||
// }
|
||||
//
|
||||
// skipPixels -= segmentLengthRemaining;
|
||||
// if (skipPixels < SEGMENT_SAFETY_DISTANCE) {
|
||||
// skipPixels = SEGMENT_SAFETY_DISTANCE;
|
||||
// }
|
||||
//
|
||||
// // set the previous way point coordinates for the next loop
|
||||
// previousX = currentX;
|
||||
// previousY = currentY;
|
||||
// }
|
||||
// }
|
||||
|
||||
static TextItem renderText(float[] coordinates, String text, PathText pathText,
|
||||
int pos, int len, TextItem textItems) {
|
||||
TextItem items = textItems;
|
||||
TextItem t = null;
|
||||
// calculate the way name length plus some margin of safety
|
||||
float wayNameWidth = -1;
|
||||
float minWidth = 100;
|
||||
int skipPixels = 0;
|
||||
|
||||
// get the first way point coordinates
|
||||
int previousX = (int) coordinates[pos + 0];
|
||||
int previousY = (int) coordinates[pos + 1];
|
||||
|
||||
// find way segments long enough to draw the way name on them
|
||||
for (int i = pos + 2; i < pos + len; i += 2) {
|
||||
// get the current way point coordinates
|
||||
int currentX = (int) coordinates[i];
|
||||
int currentY = (int) coordinates[i + 1];
|
||||
|
||||
// calculate the length of the current segment (Euclidian distance)
|
||||
float diffX = currentX - previousX;
|
||||
float diffY = currentY - previousY;
|
||||
|
||||
for (int j = i + 2; j < pos + len; j += 2) {
|
||||
int nextX = (int) coordinates[j];
|
||||
int nextY = (int) coordinates[j + 1];
|
||||
|
||||
if (diffY == 0) {
|
||||
if ((currentY - nextY) != 0)
|
||||
break;
|
||||
|
||||
currentX = nextX;
|
||||
currentY = nextY;
|
||||
continue;
|
||||
} else if ((currentY - nextY) == 0)
|
||||
break;
|
||||
|
||||
float diff = diffX / diffY -
|
||||
(float) (currentX - nextX) / (currentY - nextY);
|
||||
|
||||
// skip segments with corners
|
||||
if (diff >= 0.1f || diff <= -0.1f)
|
||||
break;
|
||||
|
||||
currentX = nextX;
|
||||
currentY = nextY;
|
||||
}
|
||||
|
||||
diffX = currentX - previousX;
|
||||
diffY = currentY - previousY;
|
||||
|
||||
if (diffX < 0)
|
||||
diffX = -diffX;
|
||||
if (diffY < 0)
|
||||
diffY = -diffY;
|
||||
|
||||
if (diffX + diffY < minWidth) {
|
||||
previousX = currentX;
|
||||
previousY = currentY;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wayNameWidth > 0 && diffX + diffY < wayNameWidth) {
|
||||
previousX = currentX;
|
||||
previousY = currentY;
|
||||
continue;
|
||||
}
|
||||
|
||||
float segmentLengthInPixel = FloatMath.sqrt(diffX * diffX + diffY * diffY);
|
||||
|
||||
if (skipPixels > 0) {
|
||||
skipPixels -= segmentLengthInPixel;
|
||||
|
||||
} else if (segmentLengthInPixel > minWidth) {
|
||||
|
||||
if (wayNameWidth < 0) {
|
||||
wayNameWidth = pathText.paint.measureText(text);
|
||||
}
|
||||
|
||||
if (segmentLengthInPixel > wayNameWidth + 25) {
|
||||
|
||||
float s = (wayNameWidth + 25) / segmentLengthInPixel;
|
||||
int width, height;
|
||||
int x1, y1, x2, y2;
|
||||
|
||||
if (previousX < currentX) {
|
||||
x1 = previousX;
|
||||
y1 = previousY;
|
||||
x2 = currentX;
|
||||
y2 = currentY;
|
||||
} else {
|
||||
x1 = currentX;
|
||||
y1 = currentY;
|
||||
x2 = previousX;
|
||||
y2 = previousY;
|
||||
}
|
||||
|
||||
// estimate position of text on path
|
||||
width = (x2 - x1) / 2;
|
||||
x2 = x2 - (int) (width - s * width);
|
||||
x1 = x1 + (int) (width - s * width);
|
||||
|
||||
height = (y2 - y1) / 2;
|
||||
y2 = y2 - (int) (height - s * height);
|
||||
y1 = y1 + (int) (height - s * height);
|
||||
|
||||
short top = (short) (y1 < y2 ? y1 : y2);
|
||||
short bot = (short) (y1 < y2 ? y2 : y1);
|
||||
|
||||
boolean intersects = false;
|
||||
|
||||
for (TextItem t2 = items; t2 != null; t2 = t2.next) {
|
||||
|
||||
// check crossings
|
||||
if (GeometryUtils.lineIntersect(x1, y1, x2, y2, t2.x1, t2.y1,
|
||||
t2.x2, t2.y2)) {
|
||||
intersects = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// check overlapping labels of road with more than one
|
||||
// way
|
||||
short top2 = t2.y1 < t2.y2 ? t2.y1 : t2.y2;
|
||||
short bot2 = t2.y1 < t2.y2 ? t2.y2 : t2.y1;
|
||||
|
||||
if (x1 - 10 < t2.x2 && t2.x1 - 10 < x2 && top - 10 < bot2
|
||||
&& top2 - 10 < bot) {
|
||||
|
||||
if (t2.text.equals(text)) {
|
||||
intersects = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (intersects) {
|
||||
previousX = (int) coordinates[pos + i];
|
||||
previousY = (int) coordinates[pos + i + 1];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Log.d("mapsforge", "add " + text + " " + first + " " +
|
||||
// last);
|
||||
|
||||
if (previousX < currentX) {
|
||||
x1 = previousX;
|
||||
y1 = previousY;
|
||||
x2 = currentX;
|
||||
y2 = currentY;
|
||||
} else {
|
||||
x1 = currentX;
|
||||
y1 = currentY;
|
||||
x2 = previousX;
|
||||
y2 = previousY;
|
||||
}
|
||||
|
||||
// if (t == null)
|
||||
t = new TextItem(x1 + (x2 - x1) / 2, y1 + (y2 - y1) / 2, text,
|
||||
pathText, wayNameWidth);
|
||||
|
||||
t.x1 = (short) x1;
|
||||
t.y1 = (short) y1;
|
||||
t.x2 = (short) x2;
|
||||
t.y2 = (short) y2;
|
||||
|
||||
t.next = items;
|
||||
items = t;
|
||||
|
||||
// skipPixels = DISTANCE_BETWEEN_WAY_NAMES;
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
// store the previous way point coordinates
|
||||
previousX = currentX;
|
||||
previousY = currentY;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private WayDecorator() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user