diff --git a/docs/Changelog.md b/docs/Changelog.md index 408c65c50..50239eba4 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -3,6 +3,7 @@ ## New since 0.12.0 - Render themes: symbols on lines with billboard / rotation [#743](https://github.com/mapsforge/vtm/pull/743) +- Location texture renderer: rewrite and optimize [#750](https://github.com/mapsforge/vtm/pull/750) - Android: OpenGL ES 2.0 default for performance / stability [#749](https://github.com/mapsforge/vtm/pull/749) - `MapView.OPENGL_VERSION` - Android: threaded system initialization diff --git a/vtm-android-example/res/raw/marker.svg b/vtm-android-example/res/raw/marker.svg new file mode 100755 index 000000000..bca149b58 --- /dev/null +++ b/vtm-android-example/res/raw/marker.svg @@ -0,0 +1,15 @@ + + + other + + + + image/svg+xml + + other + + + + + + diff --git a/vtm-android-example/src/org/oscim/android/test/LocationActivity.java b/vtm-android-example/src/org/oscim/android/test/LocationActivity.java index 40e2fc1c0..43c8903fb 100644 --- a/vtm-android-example/src/org/oscim/android/test/LocationActivity.java +++ b/vtm-android-example/src/org/oscim/android/test/LocationActivity.java @@ -24,8 +24,10 @@ import android.os.Bundle; import org.oscim.core.MapPosition; import org.oscim.layers.LocationLayer; +import org.oscim.renderer.LocationCallback; public class LocationActivity extends BitmapTileActivity implements LocationListener { + private Location location; private LocationLayer locationLayer; private LocationManager locationManager; private final MapPosition mapPosition = new MapPosition(); @@ -37,7 +39,17 @@ public void onCreate(Bundle savedInstanceState) { locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); locationLayer = new LocationLayer(mMap); - locationLayer.locationRenderer.setShader("location_1_reverse"); + locationLayer.locationRenderer.setCallback(new LocationCallback() { + @Override + public boolean hasRotation() { + return location != null && location.hasBearing(); + } + + @Override + public float getRotation() { + return location != null && location.hasBearing() ? location.getBearing() : 0; + } + }); locationLayer.setEnabled(false); mMap.layers().add(locationLayer); } @@ -66,6 +78,7 @@ public void onStop() { @Override public void onLocationChanged(Location location) { + this.location = location; locationLayer.setEnabled(true); locationLayer.setPosition(location.getLatitude(), location.getLongitude(), location.getAccuracy()); diff --git a/vtm-android-example/src/org/oscim/android/test/LocationTextureActivity.java b/vtm-android-example/src/org/oscim/android/test/LocationTextureActivity.java index 2377bad09..4ee519af2 100644 --- a/vtm-android-example/src/org/oscim/android/test/LocationTextureActivity.java +++ b/vtm-android-example/src/org/oscim/android/test/LocationTextureActivity.java @@ -25,18 +25,16 @@ import android.os.Bundle; import org.oscim.backend.CanvasAdapter; import org.oscim.backend.canvas.Bitmap; -import org.oscim.backend.canvas.Color; import org.oscim.core.MapPosition; import org.oscim.layers.LocationTextureLayer; -import org.oscim.renderer.atlas.TextureAtlas; -import org.oscim.renderer.atlas.TextureRegion; -import org.oscim.renderer.bucket.TextureItem; +import org.oscim.renderer.LocationCallback; import org.oscim.utils.IOUtils; import java.io.IOException; import java.io.InputStream; public class LocationTextureActivity extends BitmapTileActivity implements LocationListener { + private Location location; private LocationTextureLayer locationLayer; private LocationManager locationManager; private final MapPosition mapPosition = new MapPosition(); @@ -47,34 +45,41 @@ public void onCreate(Bundle savedInstanceState) { locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); - // load a Bitmap/SVG from resources InputStream is = null; - Bitmap bmp = null; + Bitmap bitmapArrow = null; try { is = getResources().openRawResource(R.raw.arrow); - float scale = CanvasAdapter.getScale(); - bmp = CanvasAdapter.decodeSvgBitmap(is, (int) (60 * scale), (int) (60 * scale), 100); + bitmapArrow = CanvasAdapter.decodeSvgBitmap(is, (int) (48 * CanvasAdapter.getScale()), (int) (48 * CanvasAdapter.getScale()), 100); } catch (IOException e) { e.printStackTrace(); } finally { IOUtils.closeQuietly(is); } - // create TextureRegion from Bitmap - TextureRegion textureRegion = new TextureRegion(new TextureItem(bmp), new TextureAtlas.Rect(0, 0, bmp.getWidth(), bmp.getHeight())); - - // create LocationTextureLayer with created/loaded TextureRegion - locationLayer = new LocationTextureLayer(mMap, textureRegion); - - // set color of accuracy circle (Color.BLUE is default) - locationLayer.locationRenderer.setAccuracyColor(Color.get(50, 50, 255)); - - // set color of indicator circle (Color.RED is default) - locationLayer.locationRenderer.setIndicatorColor(Color.MAGENTA); + Bitmap bitmapMarker = null; + try { + is = getResources().openRawResource(R.raw.marker); + bitmapMarker = CanvasAdapter.decodeSvgBitmap(is, (int) (48 * CanvasAdapter.getScale()), (int) (48 * CanvasAdapter.getScale()), 100); + } catch (IOException e) { + e.printStackTrace(); + } finally { + IOUtils.closeQuietly(is); + } - // set billboard rendering for TextureRegion (false is default) - locationLayer.locationRenderer.setBillboard(false); + locationLayer = new LocationTextureLayer(mMap); + locationLayer.locationRenderer.setBitmapArrow(bitmapArrow); + locationLayer.locationRenderer.setBitmapMarker(bitmapMarker); + locationLayer.locationRenderer.setCallback(new LocationCallback() { + @Override + public boolean hasRotation() { + return location != null && location.hasBearing(); + } + @Override + public float getRotation() { + return location != null && location.hasBearing() ? location.getBearing() : 0; + } + }); locationLayer.setEnabled(false); mMap.layers().add(locationLayer); } @@ -103,8 +108,9 @@ public void onStop() { @Override public void onLocationChanged(Location location) { + this.location = location; locationLayer.setEnabled(true); - locationLayer.setPosition(location.getLatitude(), location.getLongitude(), location.getBearing(), location.getAccuracy()); + locationLayer.setPosition(location.getLatitude(), location.getLongitude(), location.getAccuracy()); // Follow location mMap.getMapPosition(mapPosition); diff --git a/vtm-app/src/org/oscim/app/location/Compass.java b/vtm-app/src/org/oscim/app/location/Compass.java index 6d357b791..7c798e9dc 100644 --- a/vtm-app/src/org/oscim/app/location/Compass.java +++ b/vtm-app/src/org/oscim/app/location/Compass.java @@ -1,7 +1,7 @@ /* * Copyright 2013 Ahmad Saleem * Copyright 2013 Hannes Janetzek - * Copyright 2016-2017 devemux86 + * Copyright 2016-2019 devemux86 * * 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 @@ -24,19 +24,17 @@ import android.view.animation.Animation; import android.view.animation.RotateAnimation; import android.widget.ImageView; - import org.oscim.app.App; import org.oscim.app.R; import org.oscim.core.MapPosition; import org.oscim.event.Event; import org.oscim.layers.Layer; import org.oscim.map.Map; -import org.oscim.renderer.LocationRenderer; +import org.oscim.renderer.LocationCallback; import org.oscim.utils.FastMath; @SuppressWarnings("deprecation") -public class Compass extends Layer implements SensorEventListener, Map.UpdateListener, - LocationRenderer.Callback { +public class Compass extends Layer implements SensorEventListener, Map.UpdateListener, LocationCallback { // static final Logger log = LoggerFactory.getLogger(Compass.class); diff --git a/vtm/resources/assets/shaders/accuracy_1.glsl b/vtm/resources/assets/shaders/accuracy_1.glsl new file mode 100644 index 000000000..0a5af58a4 --- /dev/null +++ b/vtm/resources/assets/shaders/accuracy_1.glsl @@ -0,0 +1,38 @@ +#ifdef GLES +precision mediump float; +#endif +uniform mat4 u_mvp; +uniform float u_phase; +uniform float u_scale; +attribute vec2 a_pos; +varying vec2 v_tex; + +void main() { + gl_Position = u_mvp * vec4(a_pos * u_scale * u_phase, 0.0, 1.0); + v_tex = a_pos; +} + +$$ + +#ifdef GLES +precision mediump float; +#endif +varying vec2 v_tex; +uniform float u_scale; +uniform int u_mode; +uniform vec4 u_color; + +void main() { + float len = 1.0 - length(v_tex); + if (u_mode == -1) { + gl_FragColor = u_color * len; + } else { + // outer ring + float a = smoothstep(0.0, 2.0 / u_scale, len); + // inner ring + float b = 0.8 * smoothstep(3.0 / u_scale, 4.0 / u_scale, len); + // - subtract inner from outer to create the outline + a = a - b; + gl_FragColor = u_color * a; + } +} diff --git a/vtm/src/org/oscim/layers/LocationLayer.java b/vtm/src/org/oscim/layers/LocationLayer.java index 7d89adc42..819e77dd1 100644 --- a/vtm/src/org/oscim/layers/LocationLayer.java +++ b/vtm/src/org/oscim/layers/LocationLayer.java @@ -1,7 +1,7 @@ /* * Copyright 2013 Ahmad Saleem * Copyright 2013 Hannes Janetzek - * Copyright 2016-2017 devemux86 + * Copyright 2016-2019 devemux86 * * 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 @@ -45,7 +45,7 @@ public void setEnabled(boolean enabled) { locationRenderer.animate(false); } - public void setPosition(double latitude, double longitude, double accuracy) { + public void setPosition(double latitude, double longitude, float accuracy) { double x = MercatorProjection.longitudeToX(longitude); double y = MercatorProjection.latitudeToY(latitude); double radius = accuracy / MercatorProjection.groundResolutionWithScale(latitude, 1); diff --git a/vtm/src/org/oscim/layers/LocationTextureLayer.java b/vtm/src/org/oscim/layers/LocationTextureLayer.java index 2648f4772..dce19fc01 100644 --- a/vtm/src/org/oscim/layers/LocationTextureLayer.java +++ b/vtm/src/org/oscim/layers/LocationTextureLayer.java @@ -1,6 +1,8 @@ /* + * Copyright 2013 Ahmad Saleem + * Copyright 2013 Hannes Janetzek + * Copyright 2016-2019 devemux86 * Copyright 2017-2018 Longri - * Copyright 2018 devemux86 * * 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 @@ -15,19 +17,22 @@ */ package org.oscim.layers; +import org.oscim.backend.CanvasAdapter; import org.oscim.core.MercatorProjection; import org.oscim.map.Map; import org.oscim.renderer.LocationTextureRenderer; -import org.oscim.renderer.atlas.TextureRegion; public class LocationTextureLayer extends Layer { public final LocationTextureRenderer locationRenderer; - public LocationTextureLayer(Map map, TextureRegion textureRegion) { + public LocationTextureLayer(Map map) { + this(map, CanvasAdapter.getScale()); + } + + public LocationTextureLayer(Map map, float scale) { super(map); - mRenderer = locationRenderer = new LocationTextureRenderer(map); - locationRenderer.setTextureRegion(textureRegion); + mRenderer = locationRenderer = new LocationTextureRenderer(map, this, scale); } @Override @@ -41,14 +46,11 @@ public void setEnabled(boolean enabled) { locationRenderer.animate(false); } - public void setPosition(double latitude, double longitude, float bearing, float accuracy) { + public void setPosition(double latitude, double longitude, float accuracy) { double x = MercatorProjection.longitudeToX(longitude); double y = MercatorProjection.latitudeToY(latitude); - bearing = -bearing; - while (bearing < 0) - bearing += 360; double radius = accuracy / MercatorProjection.groundResolutionWithScale(latitude, 1); - locationRenderer.setLocation(x, y, bearing, radius); + locationRenderer.setLocation(x, y, radius); locationRenderer.animate(true); } } diff --git a/vtm/src/org/oscim/renderer/LocationCallback.java b/vtm/src/org/oscim/renderer/LocationCallback.java new file mode 100644 index 000000000..4119ec599 --- /dev/null +++ b/vtm/src/org/oscim/renderer/LocationCallback.java @@ -0,0 +1,24 @@ +/* + * Copyright 2016-2019 devemux86 + * + * 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 . + */ +package org.oscim.renderer; + +public interface LocationCallback { + /** + * Useful with Android Location.hasBearing(). + */ + boolean hasRotation(); + + float getRotation(); +} diff --git a/vtm/src/org/oscim/renderer/LocationRenderer.java b/vtm/src/org/oscim/renderer/LocationRenderer.java index a8861649a..28f743b9b 100644 --- a/vtm/src/org/oscim/renderer/LocationRenderer.java +++ b/vtm/src/org/oscim/renderer/LocationRenderer.java @@ -20,7 +20,6 @@ import org.oscim.backend.CanvasAdapter; import org.oscim.backend.GL; -import org.oscim.backend.canvas.Color; import org.oscim.core.Box; import org.oscim.core.Point; import org.oscim.core.Tile; @@ -68,8 +67,8 @@ public class LocationRenderer extends LayerRenderer { private long mAnimStart; private boolean mCenter; - private Callback mCallback; - private final float[] mColors = new float[4]; + private LocationCallback mCallback; + private int mColor = COLOR; private final Point mLocation = new Point(Double.NaN, Double.NaN); private double mRadius; private int mShowAccuracyZoom = SHOW_ACCURACY_ZOOM; @@ -82,19 +81,13 @@ public LocationRenderer(Map map, Layer layer, float scale) { mMap = map; mLayer = layer; mScale = scale; - - float a = Color.aToFloat(COLOR); - mColors[0] = a * Color.rToFloat(COLOR); - mColors[1] = a * Color.gToFloat(COLOR); - mColors[2] = a * Color.bToFloat(COLOR); - mColors[3] = a; } public void setAnimate(boolean animate) { mAnimate = animate; } - public void setCallback(Callback callback) { + public void setCallback(LocationCallback callback) { mCallback = callback; } @@ -103,11 +96,7 @@ public void setCenter(boolean center) { } public void setColor(int color) { - float a = Color.aToFloat(color); - mColors[0] = a * Color.rToFloat(color); - mColors[1] = a * Color.gToFloat(color); - mColors[2] = a * Color.bToFloat(color); - mColors[3] = a; + mColor = color; } public void setLocation(double x, double y, double radius) { @@ -287,7 +276,7 @@ public void render(GLViewport v) { } else gl.uniform1i(uMode, -1); // Outside screen - GLUtils.glUniform4fv(uColor, 1, mColors); + GLUtils.setColor(uColor, mColor); gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4); } @@ -308,13 +297,4 @@ protected boolean init() { return true; } - - public interface Callback { - /** - * Usually true, can be used with e.g. Android Location.hasBearing(). - */ - boolean hasRotation(); - - float getRotation(); - } } diff --git a/vtm/src/org/oscim/renderer/LocationTextureRenderer.java b/vtm/src/org/oscim/renderer/LocationTextureRenderer.java index 1c5229785..35ac34285 100644 --- a/vtm/src/org/oscim/renderer/LocationTextureRenderer.java +++ b/vtm/src/org/oscim/renderer/LocationTextureRenderer.java @@ -1,6 +1,10 @@ /* + * Copyright 2013 Ahmad Saleem + * Copyright 2013 Hannes Janetzek + * Copyright 2016-2019 devemux86 + * Copyright 2016 ocsike + * Copyright 2017 Mathieu De Brito * Copyright 2017-2018 Longri - * Copyright 2018-2019 devemux86 * * 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 @@ -17,147 +21,125 @@ import org.oscim.backend.CanvasAdapter; import org.oscim.backend.GL; -import org.oscim.backend.canvas.Color; +import org.oscim.backend.canvas.Bitmap; import org.oscim.core.Box; import org.oscim.core.Point; -import org.oscim.core.PointF; import org.oscim.core.Tile; +import org.oscim.layers.Layer; import org.oscim.map.Map; -import org.oscim.renderer.atlas.TextureRegion; import org.oscim.renderer.bucket.SymbolBucket; import org.oscim.renderer.bucket.SymbolItem; import org.oscim.utils.FastMath; -import org.oscim.utils.geom.GeometryUtils; import org.oscim.utils.math.Interpolation; -import java.util.Locale; - import static org.oscim.backend.GLAdapter.gl; public class LocationTextureRenderer extends BucketRenderer { - private static final PointF CENTER_OFFSET = new PointF(0.5f, 0.5f); private static final long ANIM_RATE = 50; private static final long INTERVAL = 2000; - private static final float CIRCLE_SIZE = 30; - private static final int SHOW_ACCURACY_ZOOM = 13; - private static final boolean IS_MAC = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("mac"); - - private static final String V_SHADER = ("" - + "precision highp float;" - + "uniform mat4 u_mvp;" - + "uniform float u_phase;" - + "uniform float u_scale;" - + "attribute vec2 a_pos;" - + "varying vec2 v_tex;" - + "void main() {" - + " gl_Position = u_mvp * vec4(a_pos * u_scale * u_phase, 0.0, 1.0);" - + " v_tex = a_pos;" - + "}").replace("precision highp float;", IS_MAC ? "" : "precision highp float;"); - - // only circle without direction - private static final String F_SHADER = ("" - + "precision highp float;" - + "varying vec2 v_tex;" - + "uniform float u_scale;" - + "uniform float u_phase;" - + "uniform vec4 u_fill;" - + "void main() {" - + " float len = 1.0 - length(v_tex);" - /// outer ring - + " float a = smoothstep(0.0, 2.0 / u_scale, len);" - /// inner ring - + " float b = 0.8 * smoothstep(3.0 / u_scale, 4.0 / u_scale, len);" - /// center point - + " float c = 0.5 * (1.0 - smoothstep(14.0 / u_scale, 16.0 / u_scale, 1.0 - len));" - + " vec2 dir = normalize(v_tex);" - /// - subtract inner from outer to create the outline - /// - multiply by viewshed - /// - add center point - + " a = (a - (b + c)) + c;" - + " gl_FragColor = u_fill * a;" - + "}").replace("precision highp float;", IS_MAC ? "" : "precision highp float;"); - - private final SymbolBucket symbolBucket; - private final float[] box = new float[8]; - private final Point mapPoint = new Point(); - private final Map map; - private boolean initialized; - private boolean locationIsVisible; - private int shaderProgramNumber; + + public static float CIRCLE_SIZE = /*30*/0; + private static final int COLOR = 0xff3333cc; + private static final int SHOW_ACCURACY_ZOOM = 16; + + private final Map mMap; + private final Layer mLayer; + protected final float mScale; + private final SymbolBucket mSymbolLayer; + + private String mShaderFile; + protected int mShaderProgram; private int hVertexPosition; private int hMatrixPosition; private int hScale; private int hPhase; - private int uFill; - private double radius; - - private final Point indicatorPosition = new Point(); - private final Point screenPoint = new Point(); - private final Box boundingBox = new Box(); - private boolean runAnim; - private boolean animate = true; - private long animStart; - private boolean center; - private boolean update; - private float bearing; - - // properties - private TextureRegion textureRegion; - private int accuracyColor = Color.BLUE; - private int viewShedColor = Color.RED; - private boolean billboard = false; - - public LocationTextureRenderer(Map map) { - this.map = map; - symbolBucket = new SymbolBucket(); + private int uColor; + private int uMode; + + private final Point mIndicatorPosition = new Point(); + + private final Point mScreenPoint = new Point(); + private final Box mBBox = new Box(); + + private boolean mLocationIsVisible; + + private boolean mRunAnim; + private boolean mAnimate = true; + private long mAnimStart; + private boolean mCenter; + + private boolean mBillboard; + private Bitmap mBitmapArrow, mBitmapMarker; + private LocationCallback mCallback; + private int mColor = COLOR; + private final Point mLocation = new Point(Double.NaN, Double.NaN); + private double mRadius; + private int mShowAccuracyZoom = SHOW_ACCURACY_ZOOM; + + public LocationTextureRenderer(Map map, Layer layer) { + this(map, layer, CanvasAdapter.getScale()); } - public void setAccuracyColor(int color) { - this.accuracyColor = color; + public LocationTextureRenderer(Map map, Layer layer, float scale) { + mMap = map; + mLayer = layer; + mScale = scale; + + mSymbolLayer = new SymbolBucket(); } public void setAnimate(boolean animate) { - this.animate = animate; + mAnimate = animate; } public void setBillboard(boolean billboard) { - this.billboard = billboard; + mBillboard = billboard; + } + + public void setBitmapArrow(Bitmap bitmap) { + mBitmapArrow = bitmap; + } + + public void setBitmapMarker(Bitmap bitmap) { + mBitmapMarker = bitmap; + } + + public void setCallback(LocationCallback callback) { + mCallback = callback; } public void setCenter(boolean center) { - this.center = center; + mCenter = center; } - public void setIndicatorColor(int color) { - this.viewShedColor = color; + public void setColor(int color) { + mColor = color; } - public void setLocation(double x, double y, float bearing, double radius) { - update = true; - mapPoint.x = x; - mapPoint.y = y; - this.bearing = bearing; - this.radius = radius; + public void setLocation(double x, double y, double radius) { + mLocation.x = x; + mLocation.y = y; + mRadius = radius; } - public void setTextureRegion(TextureRegion textureRegion) { - this.textureRegion = textureRegion; + public void setShader(String shaderFile) { + mShaderFile = shaderFile; + mInitialized = false; } - public void setTextureRegion(TextureRegion textureRegion, boolean billboard) { - this.textureRegion = textureRegion; - this.billboard = billboard; + public void setShowAccuracyZoom(int showAccuracyZoom) { + mShowAccuracyZoom = showAccuracyZoom; } public void animate(boolean enable) { - if (runAnim == enable) + if (mRunAnim == enable) return; - runAnim = enable; + mRunAnim = enable; if (!enable) return; - if (!animate) + if (!mAnimate) return; final Runnable action = new Runnable() { @@ -165,62 +147,70 @@ public void animate(boolean enable) { @Override public void run() { - if (!runAnim) + if (!mRunAnim) return; - if (!animate) + if (!mAnimate) return; long diff = System.currentTimeMillis() - lastRun; - map.postDelayed(this, Math.min(ANIM_RATE, diff)); - map.render(); + mMap.postDelayed(this, Math.min(ANIM_RATE, diff)); + mMap.render(); lastRun = System.currentTimeMillis(); } }; - animStart = System.currentTimeMillis(); - map.postDelayed(action, ANIM_RATE); + mAnimStart = System.currentTimeMillis(); + mMap.postDelayed(action, ANIM_RATE); } private float animPhase() { - return (float) ((MapRenderer.frametime - animStart) % INTERVAL) / INTERVAL; + return (float) ((MapRenderer.frametime - mAnimStart) % INTERVAL) / INTERVAL; } @Override public synchronized void update(GLViewport v) { - if (!v.changed() && !update) - return; - // accuracy - if (!initialized) { + if (!mInitialized) { init(); - initialized = true; + mInitialized = true; + } + + if (!mLayer.isEnabled()) { + setReady(false); + return; } + + /*if (!v.changed() && isReady()) + return;*/ + setReady(true); - int width = map.getWidth(); - int height = map.getHeight(); + int width = mMap.getWidth(); + int height = mMap.getHeight(); double x, y; - if (center) { - x = (width >> 1) + width * map.viewport().getMapViewCenterX(); - y = (height >> 1) + height * map.viewport().getMapViewCenterY(); + if (mCenter) { + x = (width >> 1) + width * mMap.viewport().getMapViewCenterX(); + y = (height >> 1) + height * mMap.viewport().getMapViewCenterY(); } else { - v.getBBox(boundingBox, 0); + // clamp location to a position that can be + // safely translated to screen coordinates + v.getBBox(mBBox, 0); - x = mapPoint.x; - y = mapPoint.y; + x = mLocation.x; + y = mLocation.y; - if (!boundingBox.contains(mapPoint)) { - x = FastMath.clamp(x, boundingBox.xmin, boundingBox.xmax); - y = FastMath.clamp(y, boundingBox.ymin, boundingBox.ymax); + if (!mBBox.contains(mLocation)) { + x = FastMath.clamp(x, mBBox.xmin, mBBox.xmax); + y = FastMath.clamp(y, mBBox.ymin, mBBox.ymax); } // get position of Location in pixel relative to // screen center - v.toScreenPoint(x, y, screenPoint); + v.toScreenPoint(x, y, mScreenPoint); - x = screenPoint.x + width / 2; - y = screenPoint.y + height / 2; + x = mScreenPoint.x + (width >> 1); + y = mScreenPoint.y + (height >> 1); } // clip position to screen boundaries @@ -240,112 +230,130 @@ else if (y < 5) else visible++; - locationIsVisible = (visible == 2); + mLocationIsVisible = (visible == 2); - if (locationIsVisible) - animate(false); - else - animate(true); // set location indicator position - v.fromScreenPoint(x, y, indicatorPosition); + v.fromScreenPoint(x, y, mIndicatorPosition); - // Texture - mMapPosition.copy(v.pos); + /* Texture */ - double mx = v.pos.x; - double my = v.pos.y; - double scale = Tile.SIZE * v.pos.scale; - map.viewport().getMapExtents(box, 100); - long flip = (long) (Tile.SIZE * v.pos.scale) >> 1; + buckets.clear(); - /* check visibility */ - float symbolX = (float) ((mapPoint.x - mx) * scale); - float symbolY = (float) ((mapPoint.y - my) * scale); + if (mBitmapArrow == null || mBitmapMarker == null) + return; - if (symbolX > flip) - symbolX -= (flip << 1); - else if (symbolX < -flip) - symbolX += (flip << 1); - buckets.clear(); - if (!GeometryUtils.pointInPoly(symbolX, symbolY, box, 8, 0)) + if (!mLocationIsVisible) return; + float itx, ity; + if (mCenter) { + itx = 0; + ity = 0; + } else { + double mx = v.pos.x; + double my = v.pos.y; + double scale = Tile.SIZE * v.pos.scale; + + long flip = (long) (Tile.SIZE * v.pos.scale) >> 1; + + itx = (float) ((mLocation.x - mx) * scale); + ity = (float) ((mLocation.y - my) * scale); + + if (itx > flip) + itx -= (flip << 1); + else if (itx < -flip) + itx += (flip << 1); + } + + mMapPosition.copy(v.pos); mMapPosition.bearing = -mMapPosition.bearing; - if (textureRegion == null) - return; - SymbolItem symbolItem = SymbolItem.pool.get(); - symbolItem.set(symbolX, symbolY, textureRegion, this.bearing, this.billboard); - symbolItem.offset = CENTER_OFFSET; - symbolBucket.pushSymbol(symbolItem); - buckets.set(symbolBucket); + SymbolItem s = SymbolItem.pool.get(); + if (mCallback != null && mCallback.hasRotation()) + s.set(itx, ity, mBitmapArrow, mCallback.getRotation(), mBillboard); + else + s.set(itx, ity, mBitmapMarker, mBillboard); + mSymbolLayer.pushSymbol(s); + + buckets.set(mSymbolLayer); buckets.prepare(); - buckets.compile(true); + compile(); - update = false; } @Override - public void render(GLViewport v) { - renderAccuracyCircle(v); - super.render(v); - } - - private void init() { - int shader = GLShader.createProgram(V_SHADER, F_SHADER); - if (shader == 0) - return; - - shaderProgramNumber = shader; - hVertexPosition = gl.getAttribLocation(shader, "a_pos"); - hMatrixPosition = gl.getUniformLocation(shader, "u_mvp"); - hPhase = gl.getUniformLocation(shader, "u_phase"); - hScale = gl.getUniformLocation(shader, "u_scale"); - uFill = gl.getUniformLocation(shader, "u_fill"); - } + public synchronized void render(GLViewport v) { - private void renderAccuracyCircle(GLViewport v) { - GLState.useProgram(shaderProgramNumber); + GLState.useProgram(mShaderProgram); GLState.blend(true); GLState.test(false, false); GLState.enableVertexArrays(hVertexPosition, GLState.DISABLED); MapRenderer.bindQuadVertexVBO(hVertexPosition/*, true*/); - float radius = 10; + float radius = CIRCLE_SIZE * mScale; + boolean viewShed = false; - if (!locationIsVisible) - radius = CIRCLE_SIZE * CanvasAdapter.getScale(); - else { - if (v.pos.zoomLevel >= SHOW_ACCURACY_ZOOM) - radius = (float) (this.radius * v.pos.scale); - radius = Math.max(2, radius); + if (!mLocationIsVisible /* || pos.zoomLevel < SHOW_ACCURACY_ZOOM */) { + animate(true); + } else { + if (v.pos.zoomLevel >= mShowAccuracyZoom) + radius = (float) (mRadius * v.pos.scale); + radius = Math.max(CIRCLE_SIZE * mScale, radius); + viewShed = true; + animate(false); } gl.uniform1f(hScale, radius); - double x = indicatorPosition.x - v.pos.x; - double y = indicatorPosition.y - v.pos.y; + double x = mIndicatorPosition.x - v.pos.x; + double y = mIndicatorPosition.y - v.pos.y; double tileScale = Tile.SIZE * v.pos.scale; v.mvp.setTransScale((float) (x * tileScale), (float) (y * tileScale), 1); v.mvp.multiplyMM(v.viewproj, v.mvp); v.mvp.setAsUniform(hMatrixPosition); - if (!viewShed && animate) { + if (!viewShed && mAnimate) { float phase = Math.abs(animPhase() - 0.5f) * 2; //phase = Interpolation.fade.apply(phase); phase = Interpolation.swing.apply(phase); + gl.uniform1f(hPhase, 0.8f + phase * 0.2f); - } else + } else { gl.uniform1f(hPhase, 1); + } - if (viewShed && locationIsVisible) - GLUtils.setColor(uFill, accuracyColor, 1); - else - GLUtils.setColor(uFill, viewShedColor, 1); + if (viewShed && mLocationIsVisible) { + if (mCallback != null && mCallback.hasRotation()) + gl.uniform1i(uMode, 1); // With bearing + else + gl.uniform1i(uMode, 0); // Without bearing + } else + gl.uniform1i(uMode, -1); // Outside screen + + GLUtils.setColor(uColor, mColor); gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4); - gl.flush(); + + /* Texture */ + + super.render(v); + } + + protected boolean init() { + int program = GLShader.loadShader(mShaderFile != null ? mShaderFile : "accuracy_1"); + if (program == 0) + return false; + + mShaderProgram = program; + hVertexPosition = gl.getAttribLocation(program, "a_pos"); + hMatrixPosition = gl.getUniformLocation(program, "u_mvp"); + hPhase = gl.getUniformLocation(program, "u_phase"); + hScale = gl.getUniformLocation(program, "u_scale"); + uColor = gl.getUniformLocation(program, "u_color"); + uMode = gl.getUniformLocation(program, "u_mode"); + + return true; } }