MapView base layer is now a TileLayer
- extract MapView base layer into TileLayer - extract MapTile loading from GLRenderer - move all tile layer related classes to layers.tile.* - make Overlay subclass of InputLayer, which extends Layer
This commit is contained in:
@@ -18,8 +18,6 @@ import static android.opengl.GLES20.GL_ARRAY_BUFFER;
|
||||
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 org.oscim.generator.JobTile.STATE_NEW_DATA;
|
||||
import static org.oscim.generator.JobTile.STATE_READY;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
@@ -31,6 +29,7 @@ import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.layers.tile.MapTile;
|
||||
import org.oscim.renderer.layer.Layers;
|
||||
import org.oscim.renderer.layer.TextureItem;
|
||||
import org.oscim.renderer.overlays.RenderOverlay;
|
||||
@@ -62,7 +61,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
static int CACHE_TILES = CACHE_TILES_MAX;
|
||||
|
||||
private static MapView mMapView;
|
||||
static int screenWidth, screenHeight;
|
||||
public static int screenWidth, screenHeight;
|
||||
|
||||
private static MapViewPosition mMapViewPosition;
|
||||
private static MapPosition mMapPosition;
|
||||
@@ -76,6 +75,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
private static float[] mBoxCoords;
|
||||
|
||||
public class Matrices {
|
||||
// do not modify any of these
|
||||
public final Matrix4 viewproj = new Matrix4();
|
||||
public final Matrix4 proj = new Matrix4();
|
||||
public final Matrix4 view = new Matrix4();
|
||||
@@ -89,88 +89,14 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
//private
|
||||
static float[] mClearColor = null;
|
||||
|
||||
static int mQuadIndicesID;
|
||||
final static int maxQuads = 64;
|
||||
public static int mQuadIndicesID;
|
||||
public final static int maxQuads = 64;
|
||||
|
||||
private static boolean mUpdateColor = false;
|
||||
|
||||
// drawlock to synchronize Main- and GL-Thread
|
||||
// static ReentrantLock tilelock = new ReentrantLock();
|
||||
static ReentrantLock drawlock = new ReentrantLock();
|
||||
|
||||
// Add additional tiles that serve as placeholer when flipping
|
||||
// over date-line.
|
||||
// I dont really like this but cannot think of a better solution:
|
||||
// the other option would be to run scanbox each time for upload,
|
||||
// drawing, proxies and text layer. needing to add placeholder only
|
||||
// happens rarely, unless you live on Fidschi
|
||||
|
||||
/* package */static int mNumTileHolder;
|
||||
/* package */static TileSet mDrawTiles;
|
||||
|
||||
// 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. a little too complicated...
|
||||
for (int x = x1; x < x2; x++) {
|
||||
MapTile holder = null;
|
||||
MapTile tile = null;
|
||||
boolean found = false;
|
||||
|
||||
if (x >= 0 && x < xmax)
|
||||
continue;
|
||||
|
||||
int xx = x;
|
||||
if (x < 0)
|
||||
xx = xmax + x;
|
||||
else
|
||||
xx = x - xmax;
|
||||
|
||||
if (xx < 0 || xx >= xmax)
|
||||
continue;
|
||||
|
||||
for (int i = cnt; i < cnt + mNumTileHolder; 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;
|
||||
|
||||
holder = new MapTile(x, y, (byte) mZoom);
|
||||
holder.isVisible = true;
|
||||
holder.holder = tile;
|
||||
tile.isVisible = true;
|
||||
tiles[cnt + mNumTileHolder++] = holder;
|
||||
}
|
||||
}
|
||||
};
|
||||
public static ReentrantLock drawlock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* @param mapView
|
||||
@@ -204,8 +130,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
mUpdateColor = true;
|
||||
}
|
||||
|
||||
private static int uploadCnt = 0;
|
||||
|
||||
public static boolean uploadLayers(Layers layers, int newSize,
|
||||
boolean addFill) {
|
||||
|
||||
@@ -258,7 +182,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void checkBufferUsage(boolean force) {
|
||||
public static void checkBufferUsage(boolean force) {
|
||||
// try to clear some unused vbo when exceding limit
|
||||
|
||||
if (!force && mBufferMemoryUsage < LIMIT_BUFFERS) {
|
||||
@@ -300,100 +224,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
private static Object tilelock = new Object();
|
||||
|
||||
/** set tile isVisible flag true for tiles that intersect view */
|
||||
private static void updateTileVisibility() {
|
||||
MapPosition pos = mMapPosition;
|
||||
MapTile[] tiles = mDrawTiles.tiles;
|
||||
|
||||
// lock tiles while updating isVisible state
|
||||
synchronized (GLRenderer.tilelock) {
|
||||
int tileZoom = tiles[0].zoomLevel;
|
||||
|
||||
for (int i = 0; i < mDrawTiles.cnt; i++)
|
||||
tiles[i].isVisible = false;
|
||||
|
||||
// count placeholder tiles
|
||||
mNumTileHolder = 0;
|
||||
|
||||
// check visibile tiles
|
||||
mScanBox.scan(pos.x, pos.y, pos.scale, tileZoom, mBoxCoords);
|
||||
}
|
||||
}
|
||||
|
||||
private static void uploadTileData(MapTile tile) {
|
||||
tile.state = STATE_READY;
|
||||
|
||||
if (tile.layers == null)
|
||||
return;
|
||||
|
||||
int newSize = tile.layers.getSize();
|
||||
if (newSize > 0) {
|
||||
|
||||
if (tile.layers.vbo == null)
|
||||
tile.layers.vbo = BufferObject.get(newSize);
|
||||
|
||||
if (!uploadLayers(tile.layers, newSize, true)) {
|
||||
Log.d(TAG, "BUG uploadTileData " + tile + " failed!");
|
||||
|
||||
BufferObject.release(tile.layers.vbo);
|
||||
tile.layers.vbo = null;
|
||||
tile.layers.clear();
|
||||
tile.layers = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** compile tile layer data and upload to VBOs */
|
||||
private static void compileTileLayers(MapTile[] tiles, int tileCnt) {
|
||||
uploadCnt = 0;
|
||||
for (int i = 0; i < tileCnt; i++) {
|
||||
MapTile tile = tiles[i];
|
||||
|
||||
if (!tile.isVisible)
|
||||
continue;
|
||||
|
||||
if (tile.state == STATE_READY)
|
||||
continue;
|
||||
|
||||
if (tile.state == STATE_NEW_DATA) {
|
||||
uploadTileData(tile);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tile.holder != null) {
|
||||
// load tile that is referenced by this holder
|
||||
if (tile.holder.state == STATE_NEW_DATA)
|
||||
uploadTileData(tile.holder);
|
||||
|
||||
tile.state = tile.holder.state;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check near relatives than can serve as proxy
|
||||
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
|
||||
MapTile rel = tile.rel.parent.tile;
|
||||
if (rel.state == STATE_NEW_DATA)
|
||||
uploadTileData(rel);
|
||||
|
||||
// dont load child proxies
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int c = 0; c < 4; c++) {
|
||||
if ((tile.proxies & 1 << c) == 0)
|
||||
continue;
|
||||
|
||||
MapTile rel = tile.rel.child[c].tile;
|
||||
if (rel != null && rel.state == STATE_NEW_DATA)
|
||||
uploadTileData(rel);
|
||||
}
|
||||
}
|
||||
|
||||
if (uploadCnt > 0)
|
||||
checkBufferUsage(false);
|
||||
}
|
||||
|
||||
private static void draw() {
|
||||
long start = 0;
|
||||
@@ -407,35 +238,14 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
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
|
||||
// or for a better reason see OpenGL Insights chapter 23.
|
||||
GLES20.glDepthMask(true);
|
||||
GLES20.glStencilMask(0xFF);
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT
|
||||
| GLES20.GL_DEPTH_BUFFER_BIT
|
||||
| GLES20.GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
int serial = 0;
|
||||
if (mDrawTiles != null)
|
||||
serial = mDrawTiles.serial;
|
||||
|
||||
// get current tiles to draw
|
||||
mDrawTiles = mMapView.getTileManager().getActiveTiles(mDrawTiles);
|
||||
|
||||
if (mDrawTiles == null || mDrawTiles.cnt == 0)
|
||||
return;
|
||||
|
||||
boolean tilesChanged = false;
|
||||
boolean positionChanged = false;
|
||||
|
||||
// check if the tiles have changed...
|
||||
if (serial != mDrawTiles.serial) {
|
||||
tilesChanged = true;
|
||||
// FIXME needed?
|
||||
positionChanged = true;
|
||||
}
|
||||
boolean tilesChanged = true;
|
||||
boolean positionChanged = true;
|
||||
|
||||
// get current MapPosition, set mBoxCoords (mapping of screen to model
|
||||
// coordinates)
|
||||
@@ -457,38 +267,22 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
int tileCnt = mDrawTiles.cnt;
|
||||
MapTile[] tiles = mDrawTiles.tiles;
|
||||
|
||||
if (positionChanged)
|
||||
updateTileVisibility();
|
||||
|
||||
tileCnt += mNumTileHolder;
|
||||
|
||||
/* prepare tile for rendering */
|
||||
compileTileLayers(tiles, tileCnt);
|
||||
|
||||
tilesChanged |= (uploadCnt > 0);
|
||||
|
||||
/* update overlays */
|
||||
RenderOverlay[] overlays = mMapView.getOverlayManager().getRenderLayers();
|
||||
|
||||
for (int i = 0, n = overlays.length; i < n; i++)
|
||||
overlays[i].update(mMapPosition, positionChanged, tilesChanged, mMatrices);
|
||||
|
||||
/* draw base layer */
|
||||
TileRenderer.draw(tiles, tileCnt, pos, mMatrices);
|
||||
|
||||
/* draw overlays */
|
||||
for (int i = 0, n = overlays.length; i < n; i++) {
|
||||
RenderOverlay renderOverlay = overlays[i];
|
||||
RenderOverlay renderLayer = overlays[i];
|
||||
|
||||
if (renderOverlay.newData) {
|
||||
renderOverlay.compile();
|
||||
renderOverlay.newData = false;
|
||||
if (renderLayer.newData) {
|
||||
renderLayer.compile();
|
||||
renderLayer.newData = false;
|
||||
}
|
||||
if (renderOverlay.isReady)
|
||||
renderOverlay.render(mMapPosition, mMatrices);
|
||||
if (renderLayer.isReady)
|
||||
renderLayer.render(mMapPosition, mMatrices);
|
||||
}
|
||||
|
||||
if (MapView.debugFrameTime) {
|
||||
@@ -506,44 +300,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
return ((t.tileX % 4) + (t.tileY % 4 * 4) + 1);
|
||||
}
|
||||
|
||||
// get a TileSet of currently visible tiles
|
||||
public static TileSet getVisibleTiles(TileSet td) {
|
||||
if (mDrawTiles == null)
|
||||
return td;
|
||||
|
||||
// ensure tiles keep visible state
|
||||
synchronized (GLRenderer.tilelock) {
|
||||
MapTile[] newTiles = mDrawTiles.tiles;
|
||||
int cnt = mDrawTiles.cnt;
|
||||
|
||||
if (td == null)
|
||||
td = new TileSet(newTiles.length);
|
||||
|
||||
// unlock previous tiles
|
||||
for (int i = 0; i < td.cnt; i++)
|
||||
td.tiles[i].unlock();
|
||||
|
||||
// lock tiles to not be removed from cache
|
||||
td.cnt = 0;
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
MapTile t = newTiles[i];
|
||||
if (t.isVisible && t.state == STATE_READY) {
|
||||
t.lock();
|
||||
td.tiles[td.cnt++] = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
return td;
|
||||
}
|
||||
|
||||
public static void releaseTiles(TileSet td) {
|
||||
for (int i = 0; i < td.cnt; i++) {
|
||||
td.tiles[i].unlock();
|
||||
td.tiles[i] = null;
|
||||
}
|
||||
td.cnt = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
|
||||
Log.d(TAG, "SurfaceChanged:" + mNewSurface + " " + width + "x" + height);
|
||||
@@ -608,7 +364,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
mBufferMemoryUsage = 0;
|
||||
mDrawTiles = null;
|
||||
//mDrawTiles = null;
|
||||
|
||||
int numTiles = (screenWidth / (Tile.SIZE / 2) + 2)
|
||||
* (screenHeight / (Tile.SIZE / 2) + 2);
|
||||
@@ -624,18 +380,21 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
|
||||
mMapView.redrawMap(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
// String ext = GLES20.glGetString(GLES20.GL_EXTENSIONS);
|
||||
// Log.d(TAG, "Extensions: " + ext);
|
||||
|
||||
// classes that require GL context for initialization
|
||||
public static void initRenderer() {
|
||||
LineRenderer.init();
|
||||
LineTexRenderer.init();
|
||||
PolygonRenderer.init();
|
||||
TextureRenderer.init();
|
||||
|
||||
TextureItem.init(10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
// Log.d(TAG, GLES20.glGetString(GLES20.GL_EXTENSIONS));
|
||||
|
||||
// classes that require GL context for initialization
|
||||
initRenderer();
|
||||
|
||||
mNewSurface = true;
|
||||
}
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012, 2013 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.renderer;
|
||||
|
||||
import org.oscim.generator.JobTile;
|
||||
import org.oscim.renderer.layer.Layers;
|
||||
import org.oscim.renderer.layer.TextItem;
|
||||
|
||||
/**
|
||||
* Extends Tile class for concurrent use in TileManager,
|
||||
* TileGenerator and GLRenderer threads.
|
||||
*/
|
||||
public final class MapTile extends JobTile {
|
||||
|
||||
public double x;
|
||||
public double y;
|
||||
|
||||
/**
|
||||
* Tile data set by TileGenerator.
|
||||
*/
|
||||
public TextItem labels;
|
||||
public Layers layers;
|
||||
|
||||
/**
|
||||
* Tile is in view region. Set by GLRenderer.
|
||||
*/
|
||||
public boolean isVisible;
|
||||
|
||||
/**
|
||||
* Pointer to access relatives in QuadTree
|
||||
*/
|
||||
public QuadTree rel;
|
||||
|
||||
int lastDraw = 0;
|
||||
|
||||
// keep track which tiles are locked as proxy for this tile
|
||||
public final static int PROXY_CHILD1 = 1 << 0;
|
||||
public final static int PROXY_CHILD2 = 1 << 1;
|
||||
public final static int PROXY_CHILD3 = 1 << 2;
|
||||
public final static int PROXY_CHILD4 = 1 << 3;
|
||||
public final static int PROXY_PARENT = 1 << 4;
|
||||
public final static int PROXY_GRAMPA = 1 << 5;
|
||||
public final static int PROXY_HOLDER = 1 << 6;
|
||||
|
||||
public byte proxies;
|
||||
|
||||
// check which labels were joined
|
||||
// public final static int JOIN_T = 1 << 0;
|
||||
// public final static int JOIN_B = 1 << 1;
|
||||
// public final static int JOIN_L = 1 << 2;
|
||||
// public final static int JOIN_R = 1 << 3;
|
||||
// public final static int JOINED = 15;
|
||||
// public byte joined;
|
||||
|
||||
// counting the tiles that use this tile as proxy
|
||||
byte refs;
|
||||
|
||||
// up to 255 Threads may lock a tile
|
||||
byte locked;
|
||||
|
||||
// only used GLRenderer when this tile sits in for another tile.
|
||||
// e.g. x:-1,y:0,z:1 for x:1,y:0
|
||||
MapTile holder;
|
||||
|
||||
MapTile(int tileX, int tileY, byte zoomLevel) {
|
||||
super(tileX, tileY, zoomLevel);
|
||||
this.x = (double)tileX / (1 << zoomLevel);
|
||||
this.y = (double)tileY / (1 << zoomLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if tile could be referenced by another thread
|
||||
*/
|
||||
boolean isLocked() {
|
||||
return locked > 0 || refs > 0;
|
||||
}
|
||||
|
||||
void lock() {
|
||||
if (locked++ > 0)
|
||||
return;
|
||||
|
||||
// lock all tiles that could serve as proxy
|
||||
MapTile p = rel.parent.tile;
|
||||
if (p != null && (p.state != 0)) {
|
||||
proxies |= PROXY_PARENT;
|
||||
p.refs++;
|
||||
}
|
||||
|
||||
p = rel.parent.parent.tile;
|
||||
if (p != null && (p.state != 0)) {
|
||||
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.state != 0)) {
|
||||
proxies |= (1 << j);
|
||||
p.refs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
if (--locked > 0 || proxies == 0)
|
||||
return;
|
||||
|
||||
if ((proxies & PROXY_PARENT) != 0)
|
||||
rel.parent.tile.refs--;
|
||||
|
||||
if ((proxies & PROXY_GRAMPA) != 0)
|
||||
rel.parent.parent.tile.refs--;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ((proxies & (1 << i)) != 0)
|
||||
rel.child[i].tile.refs--;
|
||||
}
|
||||
proxies = 0;
|
||||
}
|
||||
|
||||
public void addLabel(TextItem t){
|
||||
t.next = labels;
|
||||
labels = t;
|
||||
}
|
||||
}
|
||||
@@ -1,148 +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.renderer;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class QuadTree {
|
||||
private static String TAG = QuadTree.class.getName();
|
||||
|
||||
// pointer to tile 0/0/0
|
||||
private static QuadTree root;
|
||||
|
||||
// parent pointer is used to link pool items
|
||||
private static QuadTree pool;
|
||||
|
||||
public QuadTree parent;
|
||||
// .... x y
|
||||
// 0 => 0 0
|
||||
// 1 => 1 0
|
||||
// 2 => 0 1
|
||||
// 3 => 1 1
|
||||
public final QuadTree[] child = new QuadTree[4];
|
||||
int refs = 0;
|
||||
byte id;
|
||||
public MapTile tile;
|
||||
|
||||
static void init() {
|
||||
pool = null;
|
||||
root = new QuadTree();
|
||||
root.parent = root;
|
||||
}
|
||||
|
||||
static boolean remove(MapTile t) {
|
||||
if (t.rel == null) {
|
||||
// Bad Things(tm) happened
|
||||
Log.d(TAG, "BUG 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;
|
||||
|
||||
// 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++;
|
||||
|
||||
QuadTree 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;
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public abstract class ScanBox {
|
||||
|
||||
protected int mZoom;
|
||||
|
||||
abstract void setVisible(int y, int x1, int x2);
|
||||
protected abstract void setVisible(int y, int x1, int x2);
|
||||
|
||||
public void scan(double x, double y, double scale, int zoom, float[] box) {
|
||||
mZoom = zoom;
|
||||
|
||||
@@ -1,600 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 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.renderer;
|
||||
|
||||
import static org.oscim.generator.JobTile.STATE_LOADING;
|
||||
import static org.oscim.generator.JobTile.STATE_NEW_DATA;
|
||||
import static org.oscim.generator.JobTile.STATE_NONE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.generator.JobTile;
|
||||
import org.oscim.generator.TileDistanceSort;
|
||||
import org.oscim.renderer.layer.TextItem;
|
||||
import org.oscim.utils.FastMath;
|
||||
import org.oscim.view.MapView;
|
||||
import org.oscim.view.MapViewPosition;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* @TODO
|
||||
* - prefetching to cache file
|
||||
* - this class should probably not be in 'renderer' -> tilemap?
|
||||
* - make it general for reuse in tile-overlays
|
||||
*/
|
||||
public class TileManager {
|
||||
static final String TAG = TileManager.class.getSimpleName();
|
||||
private final static int MAX_ZOOMLEVEL = 17;
|
||||
private final static int MIN_ZOOMLEVEL = 2;
|
||||
|
||||
// limit number tiles with new data not uploaded to GL
|
||||
// TODO this should depend on the number of tiles displayed
|
||||
private static final int MAX_TILES_IN_QUEUE = 40;
|
||||
// cache limit threshold
|
||||
private static final int CACHE_THRESHOLD = 30;
|
||||
|
||||
private final MapView mMapView;
|
||||
private final MapViewPosition mMapViewPosition;
|
||||
|
||||
private boolean mInitialized;
|
||||
|
||||
// cache for all tiles
|
||||
private MapTile[] mTiles;
|
||||
|
||||
// actual number of tiles in mTiles
|
||||
private int mTilesCount;
|
||||
|
||||
// current end position in mTiles
|
||||
private int mTilesSize;
|
||||
|
||||
// counter for tiles with new data not uploaded to GL
|
||||
private volatile int mTilesForUpload;
|
||||
|
||||
// new tile jobs for MapWorkers
|
||||
private final ArrayList<JobTile> mJobs;
|
||||
|
||||
// counter to check whether current TileSet has changed
|
||||
private static int mUpdateSerial;
|
||||
|
||||
// lock for TileSets while updating MapTile locks
|
||||
private final Object mTilelock = new Object();
|
||||
|
||||
private TileSet mCurrentTiles;
|
||||
/* package */TileSet mNewTiles;
|
||||
|
||||
private final float[] mBoxCoords = new float[8];
|
||||
|
||||
public TileManager(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
mMapViewPosition = mapView.getMapViewPosition();
|
||||
mJobs = new ArrayList<JobTile>();
|
||||
mTiles = new MapTile[GLRenderer.CACHE_TILES];
|
||||
|
||||
mTilesSize = 0;
|
||||
mTilesForUpload = 0;
|
||||
|
||||
mUpdateSerial = 0;
|
||||
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
mInitialized = false;
|
||||
// there might be some leaks in here
|
||||
// ... free static pools
|
||||
}
|
||||
|
||||
public synchronized void init(int width, int height) {
|
||||
|
||||
// sync with GLRender thread
|
||||
// ... and labeling thread?
|
||||
GLRenderer.drawlock.lock();
|
||||
|
||||
if (mInitialized) {
|
||||
// pass VBOs and VertexItems back to pools
|
||||
for (int i = 0; i < mTilesSize; i++)
|
||||
clearTile(mTiles[i]);
|
||||
}
|
||||
//else {
|
||||
// mInitialized is set when surface changed
|
||||
// and VBOs might be lost
|
||||
// VertexPool.init();
|
||||
//}
|
||||
|
||||
// clear cache index
|
||||
QuadTree.init();
|
||||
|
||||
// clear references to cached MapTiles
|
||||
Arrays.fill(mTiles, null);
|
||||
mTilesSize = 0;
|
||||
mTilesCount = 0;
|
||||
|
||||
// clear all references to previous tiles
|
||||
for (TileSet td : mTileSets) {
|
||||
Arrays.fill(td.tiles, null);
|
||||
td.cnt = 0;
|
||||
}
|
||||
|
||||
// set up TileSet large enough to hold current tiles
|
||||
int num = Math.max(width, height);
|
||||
int size = Tile.SIZE >> 1;
|
||||
int numTiles = (num * num) / (size * size) * 4;
|
||||
mNewTiles = new TileSet(numTiles);
|
||||
mCurrentTiles = new TileSet(numTiles);
|
||||
Log.d(TAG, "max tiles: " + numTiles);
|
||||
|
||||
mInitialized = true;
|
||||
|
||||
GLRenderer.drawlock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Update mCurrentTiles TileSet of currently visible tiles.
|
||||
* 2. Add not yet loaded (or loading) tiles to JobQueue.
|
||||
* 3. Manage cache
|
||||
*
|
||||
* @param pos
|
||||
* current MapPosition
|
||||
*/
|
||||
public synchronized void update(MapPosition pos) {
|
||||
// clear JobQueue and set tiles to state == NONE.
|
||||
// one could also append new tiles and sort in JobQueue
|
||||
// but this has the nice side-effect that MapWorkers dont
|
||||
// start with old jobs while new jobs are calculated, which
|
||||
// should increase the chance that they are free when new
|
||||
// jobs come in.
|
||||
mMapView.addJobs(null);
|
||||
|
||||
// load some tiles more than currently visible (* 0.75)
|
||||
double scale = pos.scale * 0.9f;
|
||||
|
||||
int tileZoom = FastMath.clamp(pos.zoomLevel, MIN_ZOOMLEVEL, MAX_ZOOMLEVEL);
|
||||
|
||||
mMapViewPosition.getMapViewProjection(mBoxCoords);
|
||||
|
||||
// scan visible tiles. callback function calls 'addTile'
|
||||
// which updates mNewTiles
|
||||
mNewTiles.cnt = 0;
|
||||
mScanBox.scan(pos.x, pos.y, scale, tileZoom, mBoxCoords);
|
||||
|
||||
MapTile[] newTiles = mNewTiles.tiles;
|
||||
MapTile[] curTiles = mCurrentTiles.tiles;
|
||||
|
||||
boolean changed = (mNewTiles.cnt != mCurrentTiles.cnt);
|
||||
|
||||
Arrays.sort(mNewTiles.tiles, 0, mNewTiles.cnt, TileSet.coordComparator);
|
||||
|
||||
if (!changed) {
|
||||
for (int i = 0, n = mNewTiles.cnt; i < n; i++) {
|
||||
if (newTiles[i] != curTiles[i]) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
synchronized (mTilelock) {
|
||||
// lock new tiles
|
||||
for (int i = 0, n = mNewTiles.cnt; i < n; i++)
|
||||
newTiles[i].lock();
|
||||
|
||||
// unlock previous tiles
|
||||
for (int i = 0, n = mCurrentTiles.cnt; i < n; i++) {
|
||||
curTiles[i].unlock();
|
||||
curTiles[i] = null;
|
||||
}
|
||||
|
||||
// make new tiles current
|
||||
TileSet tmp = mCurrentTiles;
|
||||
mCurrentTiles = mNewTiles;
|
||||
mNewTiles = tmp;
|
||||
|
||||
mUpdateSerial++;
|
||||
}
|
||||
|
||||
// request rendering as tiles changed
|
||||
mMapView.render();
|
||||
}
|
||||
|
||||
/* Add tile jobs to queue */
|
||||
if (mJobs.isEmpty())
|
||||
return;
|
||||
|
||||
JobTile[] jobs = new JobTile[mJobs.size()];
|
||||
jobs = mJobs.toArray(jobs);
|
||||
updateTileDistances(jobs, jobs.length, pos);
|
||||
|
||||
// sets tiles to state == LOADING
|
||||
mMapView.addJobs(jobs);
|
||||
mJobs.clear();
|
||||
|
||||
/* limit cache items */
|
||||
int remove = mTilesCount - GLRenderer.CACHE_TILES;
|
||||
|
||||
if (remove > CACHE_THRESHOLD ||
|
||||
mTilesForUpload > MAX_TILES_IN_QUEUE)
|
||||
|
||||
limitCache(pos, remove);
|
||||
}
|
||||
|
||||
// need to keep track of TileSets to clear on reset...
|
||||
private static ArrayList<TileSet> mTileSets = new ArrayList<TileSet>(2);
|
||||
|
||||
public TileSet getActiveTiles(TileSet td) {
|
||||
if (mCurrentTiles == null)
|
||||
return td;
|
||||
|
||||
if (td != null && td.serial == mUpdateSerial)
|
||||
return td;
|
||||
|
||||
// dont flip new/currentTiles while copying
|
||||
synchronized (mTilelock) {
|
||||
MapTile[] newTiles = mCurrentTiles.tiles;
|
||||
int cnt = mCurrentTiles.cnt;
|
||||
|
||||
// lock tiles (and their proxies) to not be removed from cache
|
||||
for (int i = 0; i < cnt; i++)
|
||||
newTiles[i].lock();
|
||||
|
||||
MapTile[] nextTiles;
|
||||
|
||||
if (td == null) {
|
||||
td = new TileSet(newTiles.length);
|
||||
mTileSets.add(td);
|
||||
}
|
||||
|
||||
nextTiles = td.tiles;
|
||||
|
||||
// unlock previously active tiles
|
||||
for (int i = 0, n = td.cnt; i < n; i++)
|
||||
nextTiles[i].unlock();
|
||||
|
||||
// copy newTiles to nextTiles
|
||||
System.arraycopy(newTiles, 0, nextTiles, 0, cnt);
|
||||
|
||||
td.serial = mUpdateSerial;
|
||||
td.cnt = cnt;
|
||||
}
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @param tiles ...
|
||||
// */
|
||||
// public void releaseTiles(TileSet tiles) {
|
||||
//
|
||||
// }
|
||||
|
||||
/* package */MapTile addTile(int x, int y, int zoomLevel) {
|
||||
MapTile tile;
|
||||
|
||||
tile = QuadTree.getTile(x, y, zoomLevel);
|
||||
|
||||
if (tile == null) {
|
||||
tile = new MapTile(x, y, (byte) zoomLevel);
|
||||
QuadTree.add(tile);
|
||||
mJobs.add(tile);
|
||||
addToCache(tile);
|
||||
} else if (!tile.isActive()) {
|
||||
mJobs.add(tile);
|
||||
}
|
||||
|
||||
if (zoomLevel > 2) {
|
||||
boolean add = false;
|
||||
|
||||
// prefetch parent
|
||||
MapTile p = tile.rel.parent.tile;
|
||||
|
||||
if (p == null) {
|
||||
p = new MapTile(x >> 1, y >> 1, (byte) (zoomLevel - 1));
|
||||
QuadTree.add(p);
|
||||
addToCache(p);
|
||||
add = true;
|
||||
}
|
||||
|
||||
if (add || !p.isActive()) {
|
||||
// hack to not add tile twice
|
||||
p.state = STATE_LOADING;
|
||||
mJobs.add(p);
|
||||
}
|
||||
|
||||
if (zoomLevel > 3) {
|
||||
// prefetch grand parent
|
||||
p = tile.rel.parent.parent.tile;
|
||||
add = false;
|
||||
if (p == null) {
|
||||
p = new MapTile(x >> 2, y >> 2, (byte) (zoomLevel - 2));
|
||||
QuadTree.add(p);
|
||||
addToCache(p);
|
||||
add = true;
|
||||
}
|
||||
|
||||
if (add || !p.isActive()) {
|
||||
p.state = STATE_LOADING;
|
||||
mJobs.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tile;
|
||||
}
|
||||
|
||||
private void addToCache(MapTile tile) {
|
||||
|
||||
if (mTilesSize == mTiles.length) {
|
||||
if (mTilesSize > mTilesCount) {
|
||||
//Log.d(TAG, "repack: " + mTiles.length + " / " + mTilesCount);
|
||||
TileDistanceSort.sort(mTiles, 0, mTilesSize);
|
||||
mTilesSize = mTilesCount;
|
||||
}
|
||||
|
||||
if (mTilesSize == mTiles.length) {
|
||||
Log.d(TAG, "realloc tiles " + mTilesSize);
|
||||
MapTile[] tmp = new MapTile[mTiles.length + 20];
|
||||
System.arraycopy(mTiles, 0, tmp, 0, mTilesCount);
|
||||
mTiles = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
mTiles[mTilesSize++] = tile;
|
||||
mTilesCount++;
|
||||
}
|
||||
|
||||
private void clearTile(MapTile t) {
|
||||
if (t == null)
|
||||
return;
|
||||
|
||||
if (t.layers != null) {
|
||||
// TODO move this to layers clear
|
||||
if (t.layers.vbo != null) {
|
||||
BufferObject.release(t.layers.vbo);
|
||||
t.layers.vbo = null;
|
||||
}
|
||||
|
||||
t.layers.clear();
|
||||
t.layers = null;
|
||||
}
|
||||
|
||||
TextItem.pool.releaseAll(t.labels);
|
||||
|
||||
QuadTree.remove(t);
|
||||
t.state = STATE_NONE;
|
||||
|
||||
mTilesCount--;
|
||||
}
|
||||
|
||||
private static void updateTileDistances(Object[] tiles, int size, MapPosition mapPosition) {
|
||||
// TODO there is probably a better quad-tree distance function
|
||||
|
||||
int zoom = mapPosition.zoomLevel;
|
||||
double x = mapPosition.x;
|
||||
double y = mapPosition.y;
|
||||
|
||||
// half tile size at current zoom-level
|
||||
final double h = 1.0 / (2 << zoom);
|
||||
|
||||
//long center = (long)(h * (1 << zoom));
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
MapTile t = (MapTile) tiles[i];
|
||||
if (t == null)
|
||||
continue;
|
||||
|
||||
int diff = (t.zoomLevel - zoom);
|
||||
double dx, dy, scale;
|
||||
|
||||
if (diff == 0) {
|
||||
dx = (t.x + h) - x;
|
||||
dy = (t.y + h) - y;
|
||||
scale = 0.5f;
|
||||
} else if (diff > 0) {
|
||||
// tile zoom level is greater than current
|
||||
// NB: distance increase by the factor 2
|
||||
// with each zoom-level, so that children
|
||||
// will be kept less likely than parent tiles.
|
||||
double dh = 1.0 / (2 << t.zoomLevel);
|
||||
dx = (t.x + dh) - x;
|
||||
dy = (t.y + dh) - y;
|
||||
// add tilesize/2 with each zoom-level
|
||||
// so that children near the current
|
||||
// map position but a few levels above
|
||||
// will also be removed
|
||||
//dz = diff * h;
|
||||
scale = (1 << diff);
|
||||
} else {
|
||||
diff = -diff;
|
||||
// tile zoom level is smaller than current
|
||||
double dh = 1.0 / (2 << t.zoomLevel);
|
||||
|
||||
dx = (t.x + dh) - x;
|
||||
dy = (t.y + dh) - y;
|
||||
scale = 0.5f * (1 << diff);
|
||||
}
|
||||
if (dx < 0)
|
||||
dx = -dx;
|
||||
|
||||
if (dy < 0)
|
||||
dy = -dy;
|
||||
|
||||
t.distance = (float) ((dx + dy) * scale * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
private void limitCache(MapPosition mapPosition, int remove) {
|
||||
MapTile[] tiles = mTiles;
|
||||
int size = mTilesSize;
|
||||
|
||||
// count tiles that have new data
|
||||
mTilesForUpload = 0;
|
||||
int newTileCnt = 0;
|
||||
|
||||
// remove tiles that were never loaded
|
||||
for (int i = 0; i < size; i++) {
|
||||
MapTile t = tiles[i];
|
||||
if (t == null)
|
||||
continue;
|
||||
|
||||
if (t.state == STATE_NEW_DATA)
|
||||
newTileCnt++;
|
||||
|
||||
// make sure tile cannot be used by GL or MapWorker Thread
|
||||
if ((t.state != 0) || t.isLocked()) {
|
||||
continue;
|
||||
}
|
||||
clearTile(t);
|
||||
tiles[i] = null;
|
||||
remove--;
|
||||
}
|
||||
|
||||
if (remove > 10 || newTileCnt > MAX_TILES_IN_QUEUE) {
|
||||
updateTileDistances(tiles, size, mapPosition);
|
||||
|
||||
TileDistanceSort.sort(tiles, 0, size);
|
||||
|
||||
// sorting also repacks the 'sparse' filled array
|
||||
// so end of mTiles is at mTilesCount now
|
||||
size = mTilesSize = mTilesCount;
|
||||
|
||||
for (int i = size - 1; i >= 0 && remove > 0; i--) {
|
||||
MapTile t = tiles[i];
|
||||
if (t.isLocked()) {
|
||||
// dont remove tile used by GLRenderer, or somewhere else
|
||||
Log.d(TAG, "limitCache: tile still locked " + t + " " + t.distance);
|
||||
// try again in next run.
|
||||
//locked = true;
|
||||
//break;
|
||||
} else if (t.state == STATE_LOADING) {
|
||||
// NOTE: when set loading to false the tile could be
|
||||
// added to load queue again while still processed in
|
||||
// TileGenerator => need tile.cancel flag.
|
||||
// t.isLoading = false;
|
||||
Log.d(TAG, "limitCache: cancel loading " + t + " " + t.distance);
|
||||
} else {
|
||||
if (t.state == STATE_NEW_DATA)
|
||||
newTileCnt--;
|
||||
|
||||
remove--;
|
||||
clearTile(t);
|
||||
tiles[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
remove = (newTileCnt - MAX_TILES_IN_QUEUE) + 10;
|
||||
//int r = remove;
|
||||
for (int i = size - 1; i >= 0 && remove > 0; i--) {
|
||||
MapTile t = tiles[i];
|
||||
if (t != null && t.state == STATE_NEW_DATA) {
|
||||
if (!t.isLocked()) {
|
||||
clearTile(t);
|
||||
tiles[i] = null;
|
||||
remove--;
|
||||
newTileCnt--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mTilesForUpload += newTileCnt;
|
||||
//Log.d(TAG, "cleanup load queue " + tilesForUpload + "/" + r + " - " + remove);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called from MapWorker Thread when tile is loaded by TileGenerator
|
||||
*
|
||||
* @param jobTile
|
||||
* Tile ready for upload to GL
|
||||
* @return ... caller does not care
|
||||
*/
|
||||
public synchronized boolean passTile(JobTile jobTile) {
|
||||
MapTile tile = (MapTile) jobTile;
|
||||
|
||||
if (tile.state != STATE_LOADING) {
|
||||
// - should rather be STATE_FAILED
|
||||
// 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: failed loading " + tile);
|
||||
return true;
|
||||
}
|
||||
|
||||
//if (tile.vbo != null) {
|
||||
// // BAD Things(tm) happend: tile is already loaded
|
||||
// Log.d(TAG, "BUG: tile loaded before " + tile);
|
||||
// return true;
|
||||
//}
|
||||
|
||||
tile.state = STATE_NEW_DATA;
|
||||
mTilesForUpload++;
|
||||
|
||||
// locked means the tile is visible or referenced by
|
||||
// a tile that might be visible.
|
||||
if (tile.isLocked())
|
||||
mMapView.render();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private final ScanBox mScanBox = new ScanBox() {
|
||||
|
||||
@Override
|
||||
public void setVisible(int y, int x1, int x2) {
|
||||
MapTile[] tiles = mNewTiles.tiles;
|
||||
int cnt = mNewTiles.cnt;
|
||||
int maxTiles = tiles.length;
|
||||
|
||||
int xmax = 1 << mZoom;
|
||||
|
||||
for (int x = x1; x < x2; x++) {
|
||||
MapTile tile = null;
|
||||
|
||||
if (cnt == maxTiles) {
|
||||
Log.d(TAG, "reached maximum tiles " + maxTiles);
|
||||
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)
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if tile is already added
|
||||
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);
|
||||
tiles[cnt++] = tile;
|
||||
}
|
||||
}
|
||||
mNewTiles.cnt = cnt;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 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.renderer;
|
||||
|
||||
import static android.opengl.GLES20.GL_ARRAY_BUFFER;
|
||||
import static android.opengl.GLES20.glStencilMask;
|
||||
import static org.oscim.generator.JobTile.STATE_READY;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.renderer.GLRenderer.Matrices;
|
||||
import org.oscim.renderer.layer.Layer;
|
||||
import org.oscim.utils.FastMath;
|
||||
import org.oscim.utils.Matrix4;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
|
||||
/**
|
||||
* This class is for rendering the Line- and PolygonLayers of visible MapTiles.
|
||||
* For
|
||||
* visible tiles that do not have data available yet its parent in children
|
||||
* tiles are rendered when available.
|
||||
*
|
||||
* @author Hannes Janetzek
|
||||
*/
|
||||
public class TileRenderer {
|
||||
//private final static String TAG = TileRenderer.class.getName();
|
||||
|
||||
// used to increase polygon-offset for each tile drawn.
|
||||
private static int mDrawCnt;
|
||||
|
||||
// used to not draw a tile twice per frame.
|
||||
private static int mDrawSerial = 0;
|
||||
|
||||
private static Matrices mMatrices;
|
||||
|
||||
private static final Matrix4 mProjMatrix = new Matrix4();
|
||||
|
||||
static void draw(MapTile[] tiles, int tileCnt, MapPosition pos, Matrices m) {
|
||||
mDrawCnt = 0;
|
||||
mMatrices = m;
|
||||
|
||||
mProjMatrix.copy(m.viewproj);
|
||||
// discard z projection from tilt
|
||||
mProjMatrix.setValue(10, 0);
|
||||
mProjMatrix.setValue(14, 0);
|
||||
|
||||
GLES20.glDepthFunc(GLES20.GL_LESS);
|
||||
|
||||
// load texture for line caps
|
||||
LineRenderer.beginLines();
|
||||
|
||||
// Draw visible tiles
|
||||
for (int i = 0; i < tileCnt; i++) {
|
||||
MapTile t = tiles[i];
|
||||
if (t.isVisible && t.state == STATE_READY)
|
||||
drawTile(t, pos);
|
||||
}
|
||||
|
||||
double scale = pos.getZoomScale();
|
||||
|
||||
// Draw parent or children as proxy for visibile tiles that dont
|
||||
// have data yet. Proxies are clipped to the region where nothing
|
||||
// was drawn to depth buffer.
|
||||
// TODO draw proxies for placeholder
|
||||
for (int i = 0; i < tileCnt; i++) {
|
||||
MapTile t = tiles[i];
|
||||
if (t.isVisible && (t.state != STATE_READY) && (t.holder == null)){
|
||||
boolean preferParent = (scale > 1.5) || (pos.zoomLevel - t.zoomLevel < 0);
|
||||
drawProxyTile(t, pos, true, preferParent);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw grandparents
|
||||
for (int i = 0; i < tileCnt; i++) {
|
||||
MapTile t = tiles[i];
|
||||
if (t.isVisible && (t.state != STATE_READY) && (t.holder == null))
|
||||
drawProxyTile(t, pos, false, false);
|
||||
}
|
||||
|
||||
// make sure stencil buffer write is disabled
|
||||
glStencilMask(0x00);
|
||||
|
||||
LineRenderer.endLines();
|
||||
|
||||
mDrawSerial++;
|
||||
|
||||
// dont keep the ref...
|
||||
mMatrices = null;
|
||||
}
|
||||
|
||||
|
||||
private static void drawTile(MapTile tile, MapPosition pos) {
|
||||
// draw parents only once
|
||||
if (tile.lastDraw == mDrawSerial)
|
||||
return;
|
||||
|
||||
tile.lastDraw = mDrawSerial;
|
||||
|
||||
MapTile t = tile;
|
||||
if (t.holder != null)
|
||||
t = t.holder;
|
||||
|
||||
if (t.layers == null || t.layers.vbo == null) {
|
||||
//Log.d(TAG, "missing data " + (t.layers == null) + " " + (t.vbo == null));
|
||||
return;
|
||||
}
|
||||
|
||||
GLES20.glBindBuffer(GL_ARRAY_BUFFER, t.layers.vbo.id);
|
||||
|
||||
// place tile relative to map position
|
||||
int z = tile.zoomLevel;
|
||||
|
||||
float div = FastMath.pow(z - pos.zoomLevel);
|
||||
|
||||
double curScale = Tile.SIZE * pos.scale;
|
||||
double scale = (pos.scale / (1 << z));
|
||||
|
||||
float x = (float) ((tile.x - pos.x) * curScale);
|
||||
float y = (float) ((tile.y - pos.y) * curScale);
|
||||
|
||||
Matrices m = mMatrices;
|
||||
m.mvp.setTransScale(x, y, (float)(scale / GLRenderer.COORD_SCALE));
|
||||
|
||||
m.mvp.multiplyMM(mProjMatrix, m.mvp);
|
||||
|
||||
// set depth offset (used for clipping to tile boundaries)
|
||||
GLES20.glPolygonOffset(1, mDrawCnt++);
|
||||
if (mDrawCnt == 100)
|
||||
mDrawCnt = 0;
|
||||
|
||||
// simple line shader does not take forward shortening into account
|
||||
int simpleShader = (pos.tilt < 1 ? 1 : 0);
|
||||
|
||||
boolean clipped = false;
|
||||
|
||||
for (Layer l = t.layers.baseLayers; l != null;) {
|
||||
switch (l.type) {
|
||||
case Layer.POLYGON:
|
||||
l = PolygonRenderer.draw(pos, l, m, !clipped, true);
|
||||
clipped = true;
|
||||
break;
|
||||
|
||||
case Layer.LINE:
|
||||
if (!clipped) {
|
||||
// draw stencil buffer clip region
|
||||
PolygonRenderer.draw(pos, null, m, true, true);
|
||||
clipped = true;
|
||||
}
|
||||
l = LineRenderer.draw(t.layers, l, pos, m, div, simpleShader);
|
||||
break;
|
||||
|
||||
case Layer.TEXLINE:
|
||||
if (!clipped) {
|
||||
// draw stencil buffer clip region
|
||||
PolygonRenderer.draw(pos, null, m, true, true);
|
||||
clipped = true;
|
||||
}
|
||||
l = LineTexRenderer.draw(t.layers, l, pos, m, div);
|
||||
break;
|
||||
|
||||
default:
|
||||
// just in case
|
||||
l = l.next;
|
||||
}
|
||||
}
|
||||
|
||||
// clear clip-region and could also draw 'fade-effect'
|
||||
//PolygonRenderer.drawOver(m, true, 0x22000000);
|
||||
PolygonRenderer.drawOver(m, false, 0);
|
||||
}
|
||||
|
||||
private static int drawProxyChild(MapTile tile, MapPosition pos) {
|
||||
int drawn = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ((tile.proxies & 1 << i) == 0)
|
||||
continue;
|
||||
|
||||
MapTile c = tile.rel.child[i].tile;
|
||||
|
||||
if (c.state == STATE_READY) {
|
||||
drawTile(c, pos);
|
||||
drawn++;
|
||||
}
|
||||
}
|
||||
return drawn;
|
||||
}
|
||||
|
||||
// just FIXME!
|
||||
private static void drawProxyTile(MapTile tile, MapPosition pos, boolean parent, boolean preferParent) {
|
||||
//int diff = pos.zoomLevel - tile.zoomLevel;
|
||||
QuadTree r = tile.rel;
|
||||
MapTile proxy;
|
||||
|
||||
|
||||
if (!preferParent) {
|
||||
// prefer drawing children
|
||||
if (drawProxyChild(tile, pos) == 4)
|
||||
return;
|
||||
|
||||
if (parent) {
|
||||
// draw parent proxy
|
||||
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
|
||||
proxy = r.parent.tile;
|
||||
if (proxy.state == STATE_READY) {
|
||||
//Log.d(TAG, "1. draw parent " + proxy);
|
||||
drawTile(proxy, pos);
|
||||
}
|
||||
}
|
||||
} else if ((tile.proxies & MapTile.PROXY_GRAMPA) != 0) {
|
||||
// check if parent was already drawn
|
||||
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
|
||||
proxy = r.parent.tile;
|
||||
if (proxy.state == STATE_READY)
|
||||
return;
|
||||
}
|
||||
|
||||
proxy = r.parent.parent.tile;
|
||||
if (proxy.state == STATE_READY)
|
||||
drawTile(proxy, pos);
|
||||
}
|
||||
} else {
|
||||
// prefer drawing parent
|
||||
if (parent) {
|
||||
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
|
||||
proxy = r.parent.tile;
|
||||
if (proxy != null && proxy.state == STATE_READY) {
|
||||
//Log.d(TAG, "2. draw parent " + proxy);
|
||||
drawTile(proxy, pos);
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
drawProxyChild(tile, pos);
|
||||
|
||||
} else if ((tile.proxies & MapTile.PROXY_GRAMPA) != 0) {
|
||||
// check if parent was already drawn
|
||||
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
|
||||
proxy = r.parent.tile;
|
||||
if (proxy.state == STATE_READY)
|
||||
return;
|
||||
}
|
||||
// this will do nothing, just to check
|
||||
if (drawProxyChild(tile, pos) > 0)
|
||||
return;
|
||||
|
||||
proxy = r.parent.parent.tile;
|
||||
if (proxy.state == STATE_READY)
|
||||
drawTile(proxy, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +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.renderer;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* use with TileManager.getActiveTiles(TileSet) to get the current tiles. tiles
|
||||
* are locked to not be modifed until getActiveTiles passes them back on a
|
||||
* second invocation or TODO: implement TileManager.releaseTiles(TileSet).
|
||||
*/
|
||||
public final class TileSet {
|
||||
public int cnt = 0;
|
||||
public MapTile[] tiles;
|
||||
|
||||
int serial;
|
||||
|
||||
TileSet() {
|
||||
}
|
||||
|
||||
TileSet(int numTiles) {
|
||||
tiles = new MapTile[numTiles];
|
||||
}
|
||||
|
||||
public MapTile getTile(int x, int y){
|
||||
for (int i = 0; i < cnt; i++)
|
||||
if (tiles[i].tileX == x && tiles[i].tileY == y)
|
||||
return tiles[i];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Comparator<MapTile> coordComparator = new CoordComparator();
|
||||
|
||||
public static class CoordComparator implements Comparator<MapTile> {
|
||||
|
||||
@Override
|
||||
public int compare(MapTile lhs, MapTile rhs) {
|
||||
if (lhs.tileX == rhs.tileX) {
|
||||
if (lhs.tileY == rhs.tileY)
|
||||
return 0;
|
||||
|
||||
if (lhs.tileY < rhs.tileY)
|
||||
return 1;
|
||||
|
||||
return -1;
|
||||
}
|
||||
if (lhs.tileX < rhs.tileX)
|
||||
return 1;
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,13 @@ import java.nio.ShortBuffer;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.generator.JobTile;
|
||||
import org.oscim.layers.tile.JobTile;
|
||||
import org.oscim.layers.tile.MapTile;
|
||||
import org.oscim.layers.tile.TileRenderLayer;
|
||||
import org.oscim.layers.tile.TileSet;
|
||||
import org.oscim.renderer.GLRenderer;
|
||||
import org.oscim.renderer.GLRenderer.Matrices;
|
||||
import org.oscim.renderer.GLState;
|
||||
import org.oscim.renderer.MapTile;
|
||||
import org.oscim.renderer.TileSet;
|
||||
import org.oscim.renderer.layer.ExtrusionLayer;
|
||||
import org.oscim.utils.GlUtils;
|
||||
import org.oscim.view.MapView;
|
||||
@@ -39,8 +40,11 @@ import android.util.Log;
|
||||
public class ExtrusionOverlay extends RenderOverlay {
|
||||
private final static String TAG = ExtrusionOverlay.class.getName();
|
||||
|
||||
public ExtrusionOverlay(MapView mapView) {
|
||||
private final TileRenderLayer mTileLayer;
|
||||
|
||||
public ExtrusionOverlay(MapView mapView, org.oscim.layers.tile.TileRenderLayer tileRenderLayer) {
|
||||
super(mapView);
|
||||
mTileLayer = tileRenderLayer;
|
||||
}
|
||||
|
||||
private static int[] shaderProgram = new int[2];
|
||||
@@ -92,7 +96,10 @@ public class ExtrusionOverlay extends RenderOverlay {
|
||||
}
|
||||
|
||||
int ready = 0;
|
||||
mTileSet = mMapView.getTileManager().getActiveTiles(mTileSet);
|
||||
mTileSet = mTileLayer.getVisibleTiles(mTileSet);
|
||||
if (mTileSet == null)
|
||||
return;
|
||||
|
||||
MapTile[] tiles = mTileSet.tiles;
|
||||
// FIXME just release tiles in this case
|
||||
if (mAlpha == 0 || curPos.zoomLevel < 16) {
|
||||
@@ -135,11 +142,12 @@ public class ExtrusionOverlay extends RenderOverlay {
|
||||
for (int i = 0; i < mTileSet.cnt; i++) {
|
||||
if (!tiles[i].isVisible)
|
||||
continue;
|
||||
|
||||
MapTile t = tiles[i];
|
||||
|
||||
for (byte j = 0; j < 4; j++) {
|
||||
if ((t.proxies & (1 << j)) != 0) {
|
||||
MapTile c = t.rel.child[j].tile;
|
||||
MapTile c = t.rel.get(j);
|
||||
el = getLayer(c);
|
||||
|
||||
if (el == null || !el.compiled)
|
||||
|
||||
@@ -32,18 +32,19 @@ import java.util.HashMap;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.generator.JobTile;
|
||||
import org.oscim.graphics.Color;
|
||||
import org.oscim.graphics.Paint.Cap;
|
||||
import org.oscim.layers.tile.JobTile;
|
||||
import org.oscim.layers.tile.MapTile;
|
||||
import org.oscim.layers.tile.TileRenderLayer;
|
||||
import org.oscim.layers.tile.TileSet;
|
||||
import org.oscim.renderer.BufferObject;
|
||||
import org.oscim.renderer.GLRenderer;
|
||||
import org.oscim.renderer.GLRenderer.Matrices;
|
||||
import org.oscim.renderer.GLState;
|
||||
import org.oscim.renderer.LineRenderer;
|
||||
import org.oscim.renderer.MapTile;
|
||||
import org.oscim.renderer.PolygonRenderer;
|
||||
import org.oscim.renderer.TextureRenderer;
|
||||
import org.oscim.renderer.TileSet;
|
||||
import org.oscim.renderer.layer.Layer;
|
||||
import org.oscim.renderer.layer.Layers;
|
||||
import org.oscim.renderer.layer.LineLayer;
|
||||
@@ -199,11 +200,13 @@ public class TextOverlay extends BasicOverlay {
|
||||
|
||||
private float mSquareRadius;
|
||||
private int mRelabelCnt;
|
||||
private final TileRenderLayer mTileLayer;
|
||||
|
||||
public TextOverlay(MapView mapView) {
|
||||
public TextOverlay(MapView mapView, TileRenderLayer baseLayer) {
|
||||
super(mapView);
|
||||
mMapViewPosition = mapView.getMapViewPosition();
|
||||
|
||||
mMapViewPosition = mapView.getMapViewPosition();
|
||||
mTileLayer = baseLayer;
|
||||
layers.textureLayers = new TextLayer();
|
||||
mTmpLayer = new TextLayer();
|
||||
mActiveTiles = new HashMap<MapTile, LabelTile>();
|
||||
@@ -350,7 +353,9 @@ public class TextOverlay extends BasicOverlay {
|
||||
return false;
|
||||
|
||||
// get current tiles
|
||||
mTileSet = GLRenderer.getVisibleTiles(mTileSet);
|
||||
mTileSet = mTileLayer.getVisibleTiles(mTileSet);
|
||||
if (mTileSet == null)
|
||||
return false;
|
||||
|
||||
if (mTileSet.cnt == 0)
|
||||
return false;
|
||||
@@ -605,7 +610,7 @@ public class TextOverlay extends BasicOverlay {
|
||||
tl.labels = null;
|
||||
|
||||
// remove tile locks
|
||||
GLRenderer.releaseTiles(mTileSet);
|
||||
mTileLayer.releaseTiles(mTileSet);
|
||||
|
||||
// pass new labels for rendering
|
||||
synchronized (this) {
|
||||
|
||||
Reference in New Issue
Block a user