vtm/src/org/oscim/layers/tile/TileRenderLayer.java
2013-10-09 01:56:00 +02:00

311 lines
7.4 KiB
Java

/*
* 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.layers.tile;
import static org.oscim.layers.tile.JobTile.STATE_NEW_DATA;
import static org.oscim.layers.tile.JobTile.STATE_READY;
import org.oscim.core.MapPosition;
import org.oscim.renderer.BufferObject;
import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.GLRenderer.Matrices;
import org.oscim.renderer.overlays.RenderOverlay;
import org.oscim.utils.ScanBox;
import org.oscim.view.MapView;
import android.util.Log;
public class TileRenderLayer extends RenderOverlay {
private final static String TAG = TileRenderLayer.class.getName();
private final float[] mBoxCoords;
private final TileManager mTileManager;
public TileRenderLayer(MapView mapView, TileManager tileManager) {
super(mapView);
mTileManager = tileManager;
mBoxCoords = new float[8];
}
@Override
public void update(MapPosition curPos, boolean positionChanged, boolean tilesChanged,
Matrices matrices) {
int serial = 0;
mMapPosition.copy(curPos);
if (mDrawTiles != null)
serial = mDrawTiles.getSerial();
synchronized (tilelock) {
// get current tiles to draw
mDrawTiles = mTileManager.getActiveTiles(mDrawTiles);
}
if (mDrawTiles == null || mDrawTiles.cnt == 0)
return;
if (positionChanged)
mMapView.getMapViewPosition().getMapViewProjection(mBoxCoords);
boolean changed = false;
//boolean positionChanged = false;
// check if the tiles have changed...
if (serial != mDrawTiles.getSerial()) {
changed = true;
// FIXME needed?
//positionChanged = true;
}
int tileCnt = mDrawTiles.cnt;
MapTile[] tiles = mDrawTiles.tiles;
if (changed || positionChanged)
updateTileVisibility();
tileCnt += mNumTileHolder;
/* prepare tile for rendering */
int uploadCnt = compileTileLayers(tiles, tileCnt);
tilesChanged |= (uploadCnt > 0);
TileRenderer.draw(tiles, tileCnt, curPos, matrices);
}
@Override
public void compile() {
}
@Override
public void render(MapPosition pos, Matrices m) {
}
/** compile tile layer data and upload to VBOs */
private static int compileTileLayers(MapTile[] tiles, int tileCnt) {
int 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.item;
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.get(i);
if (rel != null && rel.state == STATE_NEW_DATA)
uploadTileData(rel);
}
}
if (uploadCnt > 0)
GLRenderer.checkBufferUsage(false);
return uploadCnt;
}
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 (!GLRenderer.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;
}
}
}
private final Object tilelock = new Object();
/** set tile isVisible flag true for tiles that intersect view */
private void updateTileVisibility() {
// lock tiles while updating isVisible state
synchronized (tilelock) {
MapPosition pos = mMapPosition;
MapTile[] tiles = mDrawTiles.tiles;
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);
}
}
// get a TileSet of currently visible tiles
public TileSet getVisibleTiles(TileSet td) {
if (mDrawTiles == null)
return td;
// ensure tiles keep visible state
synchronized (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 void releaseTiles(TileSet td) {
for (int i = 0; i < td.cnt; i++) {
td.tiles[i].unlock();
td.tiles[i] = null;
}
td.cnt = 0;
}
// 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 */int mNumTileHolder;
/* package */TileSet mDrawTiles;
// scanline fill class used to check tile visibility
private final ScanBox mScanBox = new ScanBox() {
@Override
protected 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;
}
}
};
}