Merge branch 's3db'

This commit is contained in:
Hannes Janetzek 2014-03-27 03:13:37 +01:00
commit 7673ab16e5
25 changed files with 1830 additions and 1070 deletions

View File

@ -33,11 +33,15 @@
android:label="@string/title_activity_map" > android:label="@string/title_activity_map" >
</activity> </activity>
<activity <activity
android:name="org.oscim.android.test.PathOverlayActivity" android:name="org.oscim.android.test.S3DBMapActivity"
android:label="@string/title_activity_map" > android:label="@string/title_activity_map" >
</activity> </activity>
<activity
android:name="org.oscim.android.test.PathOverlayActivity"
android:label="@string/title_activity_map" >
</activity>
<activity <activity
android:name="org.oscim.android.test.MarkerOverlayActivity" android:name="org.oscim.android.test.MarkerOverlayActivity"

View File

@ -30,7 +30,7 @@ import android.view.MenuItem;
public class BaseMapActivity extends MapActivity { public class BaseMapActivity extends MapActivity {
private final static boolean USE_CACHE = true; final static boolean USE_CACHE = true;
MapView mMapView; MapView mMapView;
VectorTileLayer mBaseLayer; VectorTileLayer mBaseLayer;
@ -70,7 +70,7 @@ public class BaseMapActivity extends MapActivity {
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
if (USE_CACHE) if (mCache != null)
mCache.dispose(); mCache.dispose();
} }

View File

@ -0,0 +1,45 @@
package org.oscim.android.test;
import org.oscim.android.cache.TileCache;
import org.oscim.layers.tile.s3db.S3DBLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;
import org.oscim.theme.VtmThemes;
import org.oscim.tiling.TileSource;
import org.oscim.tiling.source.oscimap4.OSciMap4TileSource;
import android.os.Bundle;
public class S3DBMapActivity extends BaseMapActivity {
TileCache mS3dbCache;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMap.setTheme(VtmThemes.DEFAULT);
//mMap.setTheme(VtmThemes.TRONRENDER);
//mMap.setTheme(VtmThemes.OSMARENDER);
TileSource ts = new OSciMap4TileSource("http://opensciencemap.org/tiles/s3db");
if (USE_CACHE) {
mS3dbCache = new TileCache(this, null, "s3db.db");
mS3dbCache.setCacheSize(512 * (1 << 10));
ts.setCache(mS3dbCache);
}
mMap.layers().add(new S3DBLayer(mMap, ts));
mMap.layers().add(new LabelLayer(mMap, mBaseLayer));
mMap.setMapPosition(53.08, 8.83, Math.pow(2, 14));
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mS3dbCache != null)
mS3dbCache.dispose();
}
}

View File

@ -46,6 +46,7 @@ public class Samples extends Activity {
linearLayout.addView(createButton(PathOverlayActivity.class)); linearLayout.addView(createButton(PathOverlayActivity.class));
linearLayout.addView(createButton(MarkerOverlayActivity.class)); linearLayout.addView(createButton(MarkerOverlayActivity.class));
linearLayout.addView(createButton(ThemeStylerActivity.class)); linearLayout.addView(createButton(ThemeStylerActivity.class));
linearLayout.addView(createButton(S3DBMapActivity.class));
} }
private Button createButton(final Class<?> clazz) { private Button createButton(final Class<?> clazz) {

View File

@ -266,4 +266,8 @@ public class MapTile extends Tile {
data = td; data = td;
} }
} }
public static int depthOffset(MapTile t) {
return ((t.tileX % 4) + (t.tileY % 4 * 4) + 1);
}
} }

View File

@ -196,6 +196,15 @@ public class TileManager {
* jobs come in. */ * jobs come in. */
jobQueue.clear(); jobQueue.clear();
if (pos.zoomLevel < mMinZoom) {
if (mCurrentTiles.cnt > 0 && pos.zoomLevel < mMinZoom - 4) {
synchronized (mTilelock) {
mCurrentTiles.releaseTiles();
}
}
return false;
}
int tileZoom = FastMath.clamp(pos.zoomLevel, mMinZoom, mMaxZoom); int tileZoom = FastMath.clamp(pos.zoomLevel, mMinZoom, mMaxZoom);
if (mZoomTable != null) { if (mZoomTable != null) {
@ -349,7 +358,7 @@ public class TileManager {
mJobs.add(tile); mJobs.add(tile);
} }
if ((zoomLevel > 2) && (mZoomTable == null)) { if ((zoomLevel > mMinZoom) && (mZoomTable == null)) {
/* prefetch parent */ /* prefetch parent */
MapTile p = tile.node.parent.item; MapTile p = tile.node.parent.item;
if (p == null) { if (p == null) {

View File

@ -226,6 +226,10 @@ public abstract class TileRenderer extends LayerRenderer {
for (int i = 0; i < mDrawTiles.cnt; i++) for (int i = 0; i < mDrawTiles.cnt; i++)
tiles[i].isVisible = false; tiles[i].isVisible = false;
if (tileZoom > pos.zoomLevel + 2 || tileZoom < pos.zoomLevel - 4) {
//log.debug("skip: zoomlevel diff " + (tileZoom - pos.zoomLevel));
return;
}
/* count placeholder tiles */ /* count placeholder tiles */
mProxyTileCnt = 0; mProxyTileCnt = 0;
@ -287,6 +291,7 @@ public abstract class TileRenderer extends LayerRenderer {
private final ScanBox mScanBox = new ScanBox() { private final ScanBox mScanBox = new ScanBox() {
@Override @Override
protected void setVisible(int y, int x1, int x2) { protected void setVisible(int y, int x1, int x2) {
MapTile[] tiles = mDrawTiles.tiles; MapTile[] tiles = mDrawTiles.tiles;
int cnt = mDrawTiles.cnt; int cnt = mDrawTiles.cnt;

View File

@ -0,0 +1,168 @@
package org.oscim.layers.tile.s3db;
import org.oscim.backend.canvas.Color;
import org.oscim.layers.tile.TileLayer;
import org.oscim.layers.tile.TileManager;
import org.oscim.layers.tile.TileRenderer;
import org.oscim.map.Map;
import org.oscim.renderer.ExtrusionRenderer;
import org.oscim.renderer.GLViewport;
import org.oscim.tiling.TileSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class S3DBLayer extends TileLayer {
static final Logger log = LoggerFactory.getLogger(S3DBLayer.class);
private final static int MAX_CACHE = 20;
private final static int SRC_ZOOM = 16;
private final TileSource mTileSource;
public S3DBLayer(Map map, TileSource tileSource) {
super(map,
new TileManager(map, SRC_ZOOM, SRC_ZOOM, MAX_CACHE),
new S3DBRenderer());
mTileSource = tileSource;
initLoader(2);
}
@Override
protected S3DBTileLoader createLoader() {
return new S3DBTileLoader(getManager(), mTileSource);
}
static class S3DBRenderer extends TileRenderer {
ExtrusionRenderer mExtRenderer;
public S3DBRenderer() {
mExtRenderer = new ExtrusionRenderer(this, 16, true, false);
}
@Override
protected synchronized void update(GLViewport v) {
super.update(v);
mExtRenderer.update(v);
setReady(mExtRenderer.isReady());
}
@Override
protected synchronized void render(GLViewport v) {
mExtRenderer.render(v);
}
}
static int getColor(String color, boolean roof) {
try {
return Color.parseColor(color);
} catch (Exception e) {
}
if (roof) {
if ("brown" == color)
return Color.get(120, 110, 110);
if ("red" == color)
return Color.get(255, 87, 69);
if ("green" == color)
return Color.get(150, 200, 130);
if ("blue" == color)
return Color.get(100, 50, 200);
}
if ("white" == color)
return Color.get(240, 240, 240);
if ("black" == color)
return Color.get(76, 76, 76);
if ("grey" == color || "gray" == color)
return Color.get(100, 100, 100);
if ("red" == color)
return Color.get(255, 190, 190);
if ("green" == color)
return Color.get(190, 255, 190);
if ("blue" == color)
return Color.get(190, 190, 255);
if ("yellow" == color)
return Color.get(255, 255, 175);
if ("pink" == color)
return Color.get(225, 175, 225);
if ("orange" == color)
return Color.get(255, 225, 150);
if ("brown" == color)
return Color.get(170, 130, 80);
if ("silver" == color)
return Color.get(153, 157, 160);
if ("gold" == color)
return Color.get(255, 215, 0);
if ("darkgray" == color || "darkgrey" == color)
return Color.DKGRAY;
if ("lightgray" == color || "lightgrey" == color)
return Color.LTGRAY;
if ("lightblue" == color)
return Color.get(173, 216, 230);
if ("beige" == color)
return Color.get(245, 245, 220);
if ("darkblue" == color)
return Color.get(50, 50, 189);
if ("transparent" == color)
return Color.get(64, 64, 64, 64);
log.debug("unknown color:{}", color);
return 0;
}
static int getMaterialColor(String material, boolean roof) {
if (roof) {
if ("glass" == material)
return Color.fade(Color.get(130, 224, 255), 0.9f);
}
if ("roof_tiles" == material)
return Color.get(216, 167, 111);
if ("tile" == material)
return Color.get(216, 167, 111);
if ("concrete" == material ||
"cement_block" == material)
return Color.get(210, 212, 212);
if ("metal" == material)
return Color.get(170, 130, 80);
if ("tar_paper" == material)
return Color.get(170, 130, 80);
if ("eternit" == material)
return Color.get(216, 167, 111);
if ("tin" == material)
return Color.get(170, 130, 80);
if ("asbestos" == material)
return Color.get(160, 152, 141);
if ("glass" == material)
return Color.get(130, 224, 255);
if ("slate" == material)
return Color.get(170, 130, 80);
if ("zink" == material)
return Color.get(180, 180, 180);
if ("gravel" == material)
return Color.get(170, 130, 80);
if ("copper" == material)
// same as roof color:green
return Color.get(150, 200, 130);
if ("wood" == material)
return Color.get(170, 130, 80);
if ("grass" == material)
return Color.get(170, 130, 80);
if ("stone" == material)
return Color.get(206, 207, 181);
if ("plaster" == material)
return Color.get(236, 237, 181);
if ("brick" == material)
return Color.get(255, 217, 191);
if ("stainless_steel" == material)
return Color.get(153, 157, 160);
log.debug("unknown material:{}", material);
return 0;
}
}

View File

@ -0,0 +1,124 @@
package org.oscim.layers.tile.s3db;
import static org.oscim.layers.tile.s3db.S3DBLayer.getMaterialColor;
import org.oscim.backend.canvas.Color;
import org.oscim.core.GeometryBuffer.GeometryType;
import org.oscim.core.MapElement;
import org.oscim.core.MercatorProjection;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileLoader;
import org.oscim.layers.tile.TileManager;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.renderer.elements.ExtrusionLayer;
import org.oscim.tiling.ITileDataSource;
import org.oscim.tiling.TileSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class S3DBTileLoader extends TileLoader {
static final Logger log = LoggerFactory.getLogger(S3DBTileLoader.class);
/** current TileDataSource used by this MapTileLoader */
private ITileDataSource mTileDataSource;
private ExtrusionLayer mLayers;
private ExtrusionLayer mRoofs;
private float mGroundScale;
public S3DBTileLoader(TileManager tileManager, TileSource tileSource) {
super(tileManager);
mTileDataSource = tileSource.getDataSource();
}
@Override
public void cleanup() {
mTileDataSource.destroy();
}
@Override
protected boolean loadTile(MapTile tile) {
mTile = tile;
double lat = MercatorProjection.toLatitude(tile.y);
mGroundScale = (float) MercatorProjection
.groundResolution(lat, 1 << mTile.zoomLevel);
mLayers = new ExtrusionLayer(0, mGroundScale, Color.get(255, 254, 252));
//mRoofs = new ExtrusionLayer(0, mGroundScale, Color.get(207, 209, 210));
mRoofs = new ExtrusionLayer(0, mGroundScale, Color.get(247, 249, 250));
mLayers.next = mRoofs;
ElementLayers layers = new ElementLayers();
layers.setExtrusionLayers(mLayers);
tile.data = layers;
try {
/* query database, which calls process() callback */
mTileDataSource.query(mTile, this);
} catch (Exception e) {
log.debug("{}", e);
return false;
}
return true;
}
String COLOR_KEY = "c";
String MATERIAL_KEY = "m";
String ROOF_KEY = "roof";
String ROOF_SHAPE_KEY = "roof:shape";
@Override
public void process(MapElement element) {
//log.debug("TAG {}", element.tags);
if (element.type != GeometryType.TRIS) {
log.debug("wrong type " + element.type);
return;
}
boolean isRoof = element.tags.containsKey(ROOF_KEY);
//if (isRoof)
// log.debug(element.tags.toString());
int c = 0;
if (element.tags.containsKey(COLOR_KEY)) {
c = S3DBLayer.getColor(element.tags.getValue(COLOR_KEY), isRoof);
}
if (c == 0 && element.tags.containsKey(MATERIAL_KEY)) {
c = getMaterialColor(element.tags.getValue(MATERIAL_KEY), isRoof);
}
if (c == 0) {
String roofShape = element.tags.getValue(ROOF_SHAPE_KEY);
if (isRoof && (roofShape == null || "flat".equals(roofShape)))
mRoofs.add(element);
else
mLayers.add(element);
return;
}
for (ExtrusionLayer l = mLayers; l != null; l = (ExtrusionLayer) l.next) {
if (l.color == c) {
l.add(element);
return;
}
}
ExtrusionLayer l = new ExtrusionLayer(0, mGroundScale, c);
l.next = mRoofs.next;
mRoofs.next = l;
l.add(element);
}
@Override
public void completed(QueryResult result) {
mLayers = null;
mRoofs = null;
super.completed(result);
}
}

View File

@ -34,19 +34,30 @@ import org.oscim.utils.FastMath;
public class BuildingLayer extends Layer implements TileLoaderThemeHook { public class BuildingLayer extends Layer implements TileLoaderThemeHook {
//static final Logger log = LoggerFactory.getLogger(BuildingOverlay.class); //static final Logger log = LoggerFactory.getLogger(BuildingOverlay.class);
final ExtrusionRenderer mExtLayer; private final static int MIN_ZOOM = 17;
private final int mMinZoom;
public BuildingLayer(Map map, VectorTileLayer tileLayer) { public BuildingLayer(Map map, VectorTileLayer tileLayer) {
super(map); super(map);
tileLayer.addHook(this); tileLayer.addHook(this);
mExtLayer = new ExtrusionRenderer(tileLayer.tileRenderer()) { mMinZoom = MIN_ZOOM;
mRenderer = new ExtrusionRenderer(tileLayer.tileRenderer(), MIN_ZOOM);
}
public BuildingLayer(Map map, VectorTileLayer tileLayer, int minZoom) {
super(map);
tileLayer.addHook(this);
mMinZoom = minZoom;
mRenderer = new ExtrusionRenderer(tileLayer.tileRenderer(), mMinZoom) {
private long mStartTime; private long mStartTime;
@Override @Override
public void update(GLViewport v) { public void update(GLViewport v) {
boolean show = v.pos.scale >= (1 << MIN_ZOOM); boolean show = v.pos.scale >= (1 << mMinZoom);
if (show) { if (show) {
if (mAlpha < 1) { if (mAlpha < 1) {
@ -79,17 +90,10 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
super.update(v); super.update(v);
} }
}; };
//mExtLayer.setColors(Color.LTGRAY, Color.GRAY, Color.DKGRAY);
mRenderer = mExtLayer;
} }
//private int multi;
private final float mFadeTime = 500; private final float mFadeTime = 500;
private final static int MIN_ZOOM = 17;
@Override @Override
public boolean render(MapTile tile, ElementLayers layers, MapElement element, public boolean render(MapTile tile, ElementLayers layers, MapElement element,
RenderStyle style, int level) { RenderStyle style, int level) {
@ -129,6 +133,7 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
return true; return true;
} }
//private int multi;
//@Override //@Override
//public boolean onTouchEvent(MotionEvent e) { //public boolean onTouchEvent(MotionEvent e) {
// int action = e.getAction() & MotionEvent.ACTION_MASK; // int action = e.getAction() & MotionEvent.ACTION_MASK;

View File

@ -140,9 +140,7 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
} }
public void setDataSource(ITileDataSource dataSource) { public void setDataSource(ITileDataSource dataSource) {
if (mTileDataSource != null) cleanup();
mTileDataSource.destroy();
mTileDataSource = dataSource; mTileDataSource = dataSource;
} }

View File

@ -19,8 +19,9 @@ package org.oscim.renderer;
import static org.oscim.layers.tile.MapTile.State.NEW_DATA; import static org.oscim.layers.tile.MapTile.State.NEW_DATA;
import static org.oscim.layers.tile.MapTile.State.READY; import static org.oscim.layers.tile.MapTile.State.READY;
import java.nio.ShortBuffer;
import org.oscim.backend.GL20; import org.oscim.backend.GL20;
import org.oscim.backend.canvas.Color;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.layers.tile.MapTile; import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileRenderer; import org.oscim.layers.tile.TileRenderer;
@ -37,21 +38,26 @@ public class ExtrusionRenderer extends LayerRenderer {
static final Logger log = LoggerFactory.getLogger(ExtrusionRenderer.class); static final Logger log = LoggerFactory.getLogger(ExtrusionRenderer.class);
private final TileRenderer mTileLayer; private final TileRenderer mTileLayer;
private final int mTileZoom;
private final boolean drawAlpha;
protected float mAlpha = 1; protected float mAlpha = 1;
public ExtrusionRenderer(TileRenderer tileRenderLayer) { public ExtrusionRenderer(TileRenderer tileRenderLayer, int tileZoom) {
mTileLayer = tileRenderLayer; mTileLayer = tileRenderLayer;
mTileSet = new TileSet(); mTileSet = new TileSet();
mTileZoom = tileZoom;
mMode = 0;
drawAlpha = true;
} }
private static int[] shaderProgram = new int[2]; public ExtrusionRenderer(TileRenderer tileRenderLayer, int tileZoom, boolean mesh, boolean alpha) {
private static int[] hVertexPosition = new int[2]; mTileLayer = tileRenderLayer;
private static int[] hLightPosition = new int[2]; mTileSet = new TileSet();
private static int[] hMatrix = new int[2]; mTileZoom = tileZoom;
private static int[] hColor = new int[2]; mMode = mesh ? 1 : 0;
private static int[] hAlpha = new int[2]; drawAlpha = false; //alpha;
private static int[] hMode = new int[2]; }
private boolean initialized = false; private boolean initialized = false;
@ -59,57 +65,46 @@ public class ExtrusionRenderer extends LayerRenderer {
private MapTile[] mTiles; private MapTile[] mTiles;
private int mTileCnt; private int mTileCnt;
private final static int SHADER = 0; private final int mMode;
static class Shader extends GLShader {
int uMVP, uColor, uAlpha, uMode, aPos, aLight;
public Shader(String shader) {
if (!create(shader))
return;
uMVP = getUniform("u_mvp");
uColor = getUniform("u_color");
uAlpha = getUniform("u_alpha");
uMode = getUniform("u_mode");
aPos = getAttrib("a_pos");
aLight = getAttrib("a_light");
}
}
private Shader mShader[] = { null, null };
private boolean initShader() { private boolean initShader() {
initialized = true; initialized = true;
for (int i = 0; i <= SHADER; i++) { mShader[0] = new Shader("extrusion_layer_ext");
if (i == 0) { mShader[1] = new Shader("extrusion_layer_mesh");
shaderProgram[i] = GLShader.createProgram(extrusionVertexShader,
extrusionFragmentShader);
} else {
shaderProgram[i] = GLShader.createProgram(extrusionVertexShader,
extrusionFragmentShaderZ);
}
if (shaderProgram[i] == 0) {
log.error("Could not create extrusion shader program. " + i);
return false;
}
hMatrix[i] = GL.glGetUniformLocation(shaderProgram[i], "u_mvp");
hColor[i] = GL.glGetUniformLocation(shaderProgram[i], "u_color");
hAlpha[i] = GL.glGetUniformLocation(shaderProgram[i], "u_alpha");
hMode[i] = GL.glGetUniformLocation(shaderProgram[i], "u_mode");
hVertexPosition[i] = GL.glGetAttribLocation(shaderProgram[i], "a_pos");
hLightPosition[i] = GL.glGetAttribLocation(shaderProgram[i], "a_light");
}
return true; return true;
} }
@Override @Override
protected void update(GLViewport v) { public void update(GLViewport v) {
if (!initialized && !initShader()) if (!initialized && !initShader())
return; return;
if (shaderProgram[0] == 0) if (mAlpha == 0 || v.pos.zoomLevel < mTileZoom) {
return;
if (mAlpha == 0 || v.pos.zoomLevel < 16) {
setReady(false); setReady(false);
return; return;
} }
if (mUpdateColors) {
synchronized (this) {
System.arraycopy(mNewColors, 0, mColor, 0, 16);
mUpdateColors = false;
}
}
int activeTiles = 0; int activeTiles = 0;
mTileLayer.getVisibleTiles(mTileSet); mTileLayer.getVisibleTiles(mTileSet);
MapTile[] tiles = mTileSet.tiles; MapTile[] tiles = mTileSet.tiles;
@ -120,30 +115,38 @@ public class ExtrusionRenderer extends LayerRenderer {
return; return;
} }
// keep a list of tiles available for rendering /* keep a list of tiles available for rendering */
if (mTiles == null || mTiles.length < mTileSet.cnt * 4) if (mTiles == null || mTiles.length < mTileSet.cnt * 4)
mTiles = new MapTile[mTileSet.cnt * 4]; mTiles = new MapTile[mTileSet.cnt * 4];
int zoom = tiles[0].zoomLevel; int zoom = tiles[0].zoomLevel;
ExtrusionLayer el; ExtrusionLayer el;
if (zoom == 17) { if (zoom == mTileZoom) {
for (int i = 0; i < mTileSet.cnt; i++) { for (int i = 0; i < mTileSet.cnt; i++) {
el = getLayer(tiles[i]); if (compileLayers(getLayer(tiles[i])))
mTiles[activeTiles++] = tiles[i];
}
} else if (zoom == mTileZoom + 1) {
O: for (int i = 0; i < mTileSet.cnt; i++) {
MapTile t = tiles[i].node.parent();
if (t == null)
continue;
for (MapTile c : mTiles)
if (c == t)
continue O;
el = getLayer(t);
if (el == null) if (el == null)
continue; continue;
if (!el.compiled) { if (compileLayers(el))
int numShorts = el.mNumVertices * 8; mTiles[activeTiles++] = t;
el.compile(MapRenderer.getShortBuffer(numShorts));
GLUtils.checkGlError("...");
}
if (el.compiled)
mTiles[activeTiles++] = tiles[i];
} }
} else if (zoom == 16) { } else if (zoom == mTileZoom - 1) {
// check if proxy children are ready /* check if proxy children are ready */
for (int i = 0; i < mTileSet.cnt; i++) { for (int i = 0; i < mTileSet.cnt; i++) {
MapTile t = tiles[i]; MapTile t = tiles[i];
for (byte j = 0; j < 4; j++) { for (byte j = 0; j < 4; j++) {
@ -162,6 +165,7 @@ public class ExtrusionRenderer extends LayerRenderer {
} }
mTileCnt = activeTiles; mTileCnt = activeTiles;
//log.debug("" + activeTiles + " " + zoom);
if (activeTiles > 0) if (activeTiles > 0)
setReady(true); setReady(true);
@ -169,6 +173,53 @@ public class ExtrusionRenderer extends LayerRenderer {
mTileLayer.releaseTiles(mTileSet); mTileLayer.releaseTiles(mTileSet);
} }
private boolean compileLayers(ExtrusionLayer el) {
if (el == null)
return false;
if (el.compiled)
return true;
int sumIndices = 0;
int sumVertices = 0;
for (ExtrusionLayer l = el; l != null; l = l.next()) {
sumIndices += l.sumIndices;
sumVertices += l.sumVertices;
}
if (sumIndices == 0) {
return false;
}
ShortBuffer vbuf = MapRenderer.getShortBuffer(sumVertices * 4);
ShortBuffer ibuf = MapRenderer.getShortBuffer(sumIndices);
for (ExtrusionLayer l = el; l != null; l = l.next())
l.compile(vbuf, ibuf);
int size = sumIndices * 2;
if (ibuf.position() != sumIndices) {
int pos = ibuf.position();
log.error("invalid indice size: {} {}", sumIndices, pos);
size = pos * 2;
}
el.vboIndices = BufferObject.get(GL20.GL_ELEMENT_ARRAY_BUFFER, size);
el.vboIndices.loadBufferData(ibuf.flip(), size);
el.vboIndices.unbind();
size = sumVertices * 4 * 2;
if (vbuf.position() != sumVertices * 4) {
int pos = vbuf.position();
log.error("invalid vertex size: {} {}", sumVertices, pos);
size = pos * 2;
}
el.vboVertices = BufferObject.get(GL20.GL_ARRAY_BUFFER, size);
el.vboVertices.loadBufferData(vbuf.flip(), size);
el.vboVertices.unbind();
GLUtils.checkGlError("extrusion layer");
return true;
}
private static ExtrusionLayer getLayer(MapTile t) { private static ExtrusionLayer getLayer(MapTile t) {
ElementLayers layers = t.getLayers(); ElementLayers layers = t.getLayers();
if (layers == null || !t.state(READY | NEW_DATA)) if (layers == null || !t.state(READY | NEW_DATA))
@ -179,51 +230,58 @@ public class ExtrusionRenderer extends LayerRenderer {
private final boolean debug = false; private final boolean debug = false;
private void renderCombined(int vertexPointer, ExtrusionLayer el) {
for (; el != null; el = el.next()) {
if (el.vboIndices == null)
continue;
el.vboIndices.bind();
el.vboVertices.bind();
GL.glVertexAttribPointer(vertexPointer, 3,
GL20.GL_SHORT, false, 8, 0);
int sumIndices = el.numIndices[0] + el.numIndices[1] + el.numIndices[2];
if (sumIndices > 0)
GL.glDrawElements(GL20.GL_TRIANGLES, sumIndices,
GL20.GL_UNSIGNED_SHORT, 0);
if (el.numIndices[2] > 0) {
int offset = sumIndices * 2;
GL.glDrawElements(GL20.GL_TRIANGLES, el.numIndices[4],
GL20.GL_UNSIGNED_SHORT, offset);
}
}
}
@Override @Override
protected void render(GLViewport v) { public void render(GLViewport v) {
// TODO one could render in one pass to texture and then draw the texture // TODO one could render in one pass to texture and then draw the texture
// with alpha... might be faster and would allow postprocessing outlines. // with alpha... might be faster and would allow postprocessing outlines.
MapTile[] tiles = mTiles; MapTile[] tiles = mTiles;
Shader s = mShader[mMode];
int uExtAlpha = hAlpha[SHADER];
int uExtColor = hColor[SHADER];
int uExtVertexPosition = hVertexPosition[SHADER];
int uExtLightPosition = hLightPosition[SHADER];
int uExtMatrix = hMatrix[SHADER];
int uExtMode = hMode[SHADER];
if (debug) { if (debug) {
GLState.useProgram(shaderProgram[SHADER]); s.useProgram();
GLState.enableVertexArrays(uExtVertexPosition, uExtLightPosition); //GLState.useProgram(shaderProgram[mMode]);
GL.glUniform1i(uExtMode, 0);
GLUtils.glUniform4fv(uExtColor, 4, mColor); GLState.enableVertexArrays(s.aPos, s.aLight);
GL.glUniform1i(s.uMode, 0);
GLUtils.glUniform4fv(s.uColor, 4, DEBUG_COLOR);
GL.glUniform1f(s.uAlpha, 1);
GLState.test(false, false); GLState.test(false, false);
GLState.blend(true);
for (int i = 0; i < mTileCnt; i++) { for (int i = 0; i < mTileCnt; i++) {
ExtrusionLayer el = tiles[i].getLayers().getExtrusionLayers(); ExtrusionLayer el = tiles[i].getLayers().getExtrusionLayers();
setMatrix(v, tiles[i], 0); setMatrix(v, tiles[i], 0);
v.mvp.setAsUniform(uExtMatrix); v.mvp.setAsUniform(s.uMVP);
el.vboIndices.bind(); renderCombined(s.aPos, el);
el.vboVertices.bind();
GL.glVertexAttribPointer(uExtVertexPosition, 3,
GL20.GL_SHORT, false, 8, 0);
GL.glVertexAttribPointer(uExtLightPosition, 2,
GL20.GL_UNSIGNED_BYTE, false, 8, 6);
GL.glDrawElements(GL20.GL_TRIANGLES,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]),
GL20.GL_UNSIGNED_SHORT, 0);
GL.glDrawElements(GL20.GL_LINES, el.mIndiceCnt[3],
GL20.GL_UNSIGNED_SHORT,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]) * 2);
// just a temporary reference! // just a temporary reference!
tiles[i] = null; tiles[i] = null;
@ -232,49 +290,48 @@ public class ExtrusionRenderer extends LayerRenderer {
} }
GL.glDepthMask(true); GL.glDepthMask(true);
//GL.glStencilMask(0xff);
//GL.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_STENCIL_BUFFER_BIT);
GL.glClear(GL20.GL_DEPTH_BUFFER_BIT); GL.glClear(GL20.GL_DEPTH_BUFFER_BIT);
GLState.test(true, false); GLState.test(true, false);
GLState.useProgram(shaderProgram[SHADER]); s.useProgram();
GLState.enableVertexArrays(uExtVertexPosition, -1); //GLState.useProgram(shaderProgram[mMode]);
if (v.pos.scale < (1 << 18)) { GLState.enableVertexArrays(s.aPos, -1);
// chances are high that one moves through a building GLState.blend(false);
// with scale > 2 also draw back sides in this case.
GL.glEnable(GL20.GL_CULL_FACE); GL.glEnable(GL20.GL_CULL_FACE);
}
GL.glDepthFunc(GL20.GL_LESS); GL.glDepthFunc(GL20.GL_LESS);
GL.glColorMask(false, false, false, false);
GL.glUniform1i(uExtMode, -1);
//GLUtils.glUniform4fv(uExtColor, 4, mColor);
GL.glUniform1f(uExtAlpha, mAlpha);
// draw to depth buffer //GL.glUniform1f(uExtAlpha, mAlpha);
for (int i = 0; i < mTileCnt; i++) { GL.glUniform1f(s.uAlpha, 1);
MapTile t = tiles[i];
ExtrusionLayer el = t.getLayers().getExtrusionLayers();
int d = MapRenderer.depthOffset(t) * 10;
setMatrix(v, t, d);
v.mvp.setAsUniform(uExtMatrix);
el.vboIndices.bind(); if (drawAlpha) {
el.vboVertices.bind(); GL.glColorMask(false, false, false, false);
GL.glUniform1i(s.uMode, -1);
//GLUtils.glUniform4fv(uExtColor, 4, mColor);
GL.glVertexAttribPointer(uExtVertexPosition, 3, /* draw to depth buffer */
GL20.GL_SHORT, false, 8, 0); for (int i = 0; i < mTileCnt; i++) {
MapTile t = tiles[i];
ExtrusionLayer el = t.getLayers().getExtrusionLayers();
if (el == null)
continue;
GL.glDrawElements(GL20.GL_TRIANGLES, int d = MapTile.depthOffset(t) * 10;
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]),
GL20.GL_UNSIGNED_SHORT, 0); setMatrix(v, t, d);
v.mvp.setAsUniform(s.uMVP);
renderCombined(s.aPos, el);
}
GL.glColorMask(true, true, true, true);
GL.glDepthMask(false);
GLState.blend(true);
} }
// enable color buffer, use depth mask
GLState.enableVertexArrays(uExtVertexPosition, uExtLightPosition);
GL.glColorMask(true, true, true, true);
GL.glDepthMask(false);
GLState.blend(true); GLState.blend(true);
GLState.enableVertexArrays(s.aPos, s.aLight);
float[] currentColor = null; float[] currentColor = null;
@ -282,211 +339,152 @@ public class ExtrusionRenderer extends LayerRenderer {
MapTile t = tiles[i]; MapTile t = tiles[i];
ExtrusionLayer el = t.getLayers().getExtrusionLayers(); ExtrusionLayer el = t.getLayers().getExtrusionLayers();
if (el.colors == null) { if (el == null)
currentColor = mColor; continue;
GLUtils.glUniform4fv(uExtColor, 4, currentColor);
} else if (currentColor != el.colors) { if (el.vboIndices == null)
currentColor = el.colors; continue;
GLUtils.glUniform4fv(uExtColor, 4, currentColor);
int d = 1;
if (drawAlpha) {
GL.glDepthFunc(GL20.GL_EQUAL);
d = MapTile.depthOffset(t) * 10;
} }
GL.glDepthFunc(GL20.GL_EQUAL);
int d = MapRenderer.depthOffset(t) * 10;
setMatrix(v, t, d); setMatrix(v, t, d);
v.mvp.setAsUniform(uExtMatrix); v.mvp.setAsUniform(s.uMVP);
el.vboIndices.bind(); el.vboIndices.bind();
el.vboVertices.bind(); el.vboVertices.bind();
GL.glVertexAttribPointer(uExtVertexPosition, 3, for (; el != null; el = el.next()) {
GL20.GL_SHORT, false, 8, 0);
GL.glVertexAttribPointer(uExtLightPosition, 2, if (el.colors != currentColor) {
GL20.GL_UNSIGNED_BYTE, false, 8, 6); currentColor = el.colors;
GLUtils.glUniform4fv(s.uColor, mMode == 0 ? 4 : 1,
el.colors);
}
// draw roof /* indices offset */
GL.glUniform1i(uExtMode, 0); int indexOffset = el.indexOffset;
GL.glDrawElements(GL20.GL_TRIANGLES, el.mIndiceCnt[2], /* vertex byte offset */
GL20.GL_UNSIGNED_SHORT, (el.mIndiceCnt[0] + el.mIndiceCnt[1]) * 2); int vertexOffset = el.getOffset();
// draw sides 1 GL.glVertexAttribPointer(s.aPos, 3,
GL.glUniform1i(uExtMode, 1); GL20.GL_SHORT, false, 8, vertexOffset);
GL.glDrawElements(GL20.GL_TRIANGLES, el.mIndiceCnt[0],
GL20.GL_UNSIGNED_SHORT, 0);
// draw sides 2 GL.glVertexAttribPointer(s.aLight, 2,
GL.glUniform1i(uExtMode, 2); GL20.GL_UNSIGNED_BYTE, false, 8, vertexOffset + 6);
GL.glDrawElements(GL20.GL_TRIANGLES, el.mIndiceCnt[1],
GL20.GL_UNSIGNED_SHORT, el.mIndiceCnt[0] * 2);
// drawing gl_lines with the same coordinates does not result in /* draw extruded outlines */
// same depth values as polygons, so add offset and draw gl_lequal: if (el.numIndices[0] > 0) {
GL.glDepthFunc(GL20.GL_LEQUAL); /* draw roof */
GL.glUniform1i(s.uMode, 0);
GL.glDrawElements(GL20.GL_TRIANGLES, el.numIndices[2],
GL20.GL_UNSIGNED_SHORT,
(el.numIndices[0] + el.numIndices[1]) * 2);
v.mvp.addDepthOffset(100); /* draw sides 1 */
v.mvp.setAsUniform(uExtMatrix); GL.glUniform1i(s.uMode, 1);
GL.glDrawElements(GL20.GL_TRIANGLES, el.numIndices[0],
GL20.GL_UNSIGNED_SHORT, 0);
GL.glUniform1i(uExtMode, 3); /* draw sides 2 */
GL.glDrawElements(GL20.GL_LINES, el.mIndiceCnt[3], GL.glUniform1i(s.uMode, 2);
GL20.GL_UNSIGNED_SHORT, GL.glDrawElements(GL20.GL_TRIANGLES, el.numIndices[1],
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]) * 2); GL20.GL_UNSIGNED_SHORT, el.numIndices[0] * 2);
if (drawAlpha) {
/* drawing gl_lines with the same coordinates does not
* result in same depth values as polygons, so add
* offset and draw gl_lequal: */
GL.glDepthFunc(GL20.GL_LEQUAL);
}
v.mvp.addDepthOffset(100);
v.mvp.setAsUniform(s.uMVP);
GL.glUniform1i(s.uMode, 3);
int offset = 2 * (indexOffset
+ el.numIndices[0]
+ el.numIndices[1]
+ el.numIndices[2]);
GL.glDrawElements(GL20.GL_LINES, el.numIndices[3],
GL20.GL_UNSIGNED_SHORT, offset);
}
/* draw triangle meshes */
if (el.numIndices[4] > 0) {
int offset = 2 * (indexOffset
+ el.numIndices[0]
+ el.numIndices[1]
+ el.numIndices[2]
+ el.numIndices[3]);
GL.glUniform1i(s.uMode, 4);
GL.glDrawElements(GL20.GL_TRIANGLES, el.numIndices[4],
GL20.GL_UNSIGNED_SHORT, offset);
}
}
// just a temporary reference! // just a temporary reference!
tiles[i] = null; tiles[i] = null;
} }
if (v.pos.scale < (1 << 18)) GL.glDepthMask(false);
GL.glDisable(GL20.GL_CULL_FACE); GL.glDisable(GL20.GL_CULL_FACE);
GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0); GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
mTileLayer.releaseTiles(mTileSet); mTileLayer.releaseTiles(mTileSet);
} }
private static void setMatrix(GLViewport m, private static void setMatrix(GLViewport v, MapTile tile, int delta) {
MapTile tile, int delta) {
int z = tile.zoomLevel; int z = tile.zoomLevel;
double curScale = Tile.SIZE * m.pos.scale; double curScale = Tile.SIZE * v.pos.scale;
float scale = (float) (m.pos.scale / (1 << z)); float scale = (float) (v.pos.scale / (1 << z));
float x = (float) ((tile.x - m.pos.x) * curScale); float x = (float) ((tile.x - v.pos.x) * curScale);
float y = (float) ((tile.y - m.pos.y) * curScale); float y = (float) ((tile.y - v.pos.y) * curScale);
m.mvp.setTransScale(x, y, scale / MapRenderer.COORD_SCALE); v.mvp.setTransScale(x, y, scale / MapRenderer.COORD_SCALE);
// scale height // scale height ???
m.mvp.setValue(10, scale / 10); v.mvp.setValue(10, scale / 10);
m.mvp.multiplyLhs(m.viewproj); v.mvp.multiplyLhs(v.viewproj);
m.mvp.addDepthOffset(delta); v.mvp.addDepthOffset(delta);
} }
public synchronized void setColors(float[] colors) { private static float A = 0.88f;
System.arraycopy(colors, 0, mNewColors, 0, 16); private static float R = 0xe9;
mUpdateColors = true; private static float G = 0xe8;
} private static float B = 0xe6;
private static float O = 20;
private static float S = 4;
private static float L = 0;
public synchronized void setColors(int sides, int top, int lines) { private static float[] DEBUG_COLOR = {
fillColors(sides, top, lines);
mUpdateColors = true;
}
private void fillColors(int sides, int top, int lines) {
mNewColors[0] = Color.rToFloat(top);
mNewColors[1] = Color.gToFloat(top);
mNewColors[2] = Color.bToFloat(top);
mNewColors[3] = Color.aToFloat(top);
mNewColors[4] = Color.rToFloat(sides);
mNewColors[5] = Color.gToFloat(sides);
mNewColors[6] = Color.bToFloat(sides);
mNewColors[7] = Color.aToFloat(sides);
mNewColors[8] = Color.rToFloat(sides);
mNewColors[9] = Color.gToFloat(sides);
mNewColors[10] = Color.bToFloat(sides);
mNewColors[11] = Color.aToFloat(sides);
mNewColors[12] = Color.rToFloat(lines);
mNewColors[13] = Color.gToFloat(lines);
mNewColors[14] = Color.bToFloat(lines);
mNewColors[15] = Color.aToFloat(lines);
}
private boolean mUpdateColors;
private final float[] mNewColors = new float[16];
private final float _a = 0.88f;
private final float _r = 0xe9;
private final float _g = 0xe8;
private final float _b = 0xe6;
private final float _o = 20;
private final float _s = 4;
private final float _l = 0;
private final float[] mColor = {
// roof color // roof color
_a * ((_r + _l) / 255), A * ((R + L) / 255),
_a * ((_g + _l) / 255), A * ((G + L) / 255),
_a * ((_b + _l) / 255), A * ((B + L) / 255),
0.8f, 0.8f,
// sligthly differ adjacent side // sligthly differ adjacent side
// faces to improve contrast // faces to improve contrast
_a * ((_r - _s) / 255 + 0.01f), A * ((R - S) / 255 + 0.01f),
_a * ((_g - _s) / 255 + 0.01f), A * ((G - S) / 255 + 0.01f),
_a * ((_b - _s) / 255), A * ((B - S) / 255),
_a, A,
_a * ((_r - _s) / 255), A * ((R - S) / 255),
_a * ((_g - _s) / 255), A * ((G - S) / 255),
_a * ((_b - _s) / 255), A * ((B - S) / 255),
_a, A,
// roof outline // roof outline
(_r - _o) / 255, (R - O) / 255,
(_g - _o) / 255, (G - O) / 255,
(_b - _o) / 255, (B - O) / 255,
0.9f, 0.9f,
}; };
final static String extrusionVertexShader = ""
//+ "precision mediump float;"
+ "uniform mat4 u_mvp;"
+ "uniform vec4 u_color[4];"
+ "uniform int u_mode;"
+ "uniform float u_alpha;"
+ "attribute vec4 a_pos;"
+ "attribute vec2 a_light;"
+ "varying vec4 color;"
+ "varying float depth;"
+ "const float ff = 255.0;"
+ "void main() {"
// change height by u_alpha
+ " gl_Position = u_mvp * vec4(a_pos.xy, a_pos.z * u_alpha, 1.0);"
//+ " depth = gl_Position.z;"
+ " if (u_mode == -1) ;"
// roof / depth pass
//+ " color = u_color[0] * u_alpha;"
+ " else if (u_mode == 0)"
// roof / depth pass
+ " color = u_color[0] * u_alpha;"
+ " else {"
// decrease contrast with distance
+ " if (u_mode == 1){"
// sides 1 - use 0xff00
// scale direction to -0.5<>0.5
+ " float dir = a_light.y / ff;"
+ " float z = (0.98 + gl_Position.z * 0.02);"
+ " float h = 0.9 + clamp(a_pos.z / 2000.0, 0.0, 0.1);"
+ " color = u_color[1];"
+ " color.rgb *= (0.8 + dir * 0.2) * z * h;"
+ " color *= u_alpha;"
+ " } else if (u_mode == 2){"
// sides 2 - use 0x00ff
+ " float dir = a_light.x / ff;"
+ " float z = (0.98 + gl_Position.z * 0.02);"
+ " float h = 0.9 + clamp(a_pos.z / 2000.0, 0.0, 0.1);"
+ " color = u_color[2];"
+ " color.rgb *= (0.8 + dir * 0.2) * z * h;"
+ " color *= u_alpha;"
+ " } else {"
// outline
+ " float z = (0.98 - gl_Position.z * 0.02);"
+ " color = u_color[3] * z;"
+ "}}}";
final static String extrusionFragmentShader = ""
+ "precision mediump float;"
+ "varying vec4 color;"
+ "void main() {"
+ " gl_FragColor = color;"
+ "}";
final static String extrusionFragmentShaderZ = ""
+ "precision mediump float;"
+ "varying float depth;"
+ "void main() {"
+ "float d = depth * 0.2;"
+ "if (d < 0.0)"
+ " d = -d;"
+ " gl_FragColor = vec4(1.0 - d, 1.0 - d, 1.0 - d, 1.0 - d);"
+ "}";
} }

View File

@ -41,7 +41,7 @@ public abstract class LayerRenderer {
isReady = ready; isReady = ready;
} }
protected boolean isReady() { public boolean isReady() {
return isReady; return isReady;
} }

View File

@ -25,7 +25,6 @@ import java.nio.ShortBuffer;
import org.oscim.backend.GL20; import org.oscim.backend.GL20;
import org.oscim.backend.GLAdapter; import org.oscim.backend.GLAdapter;
import org.oscim.backend.canvas.Color; import org.oscim.backend.canvas.Color;
import org.oscim.layers.tile.MapTile;
import org.oscim.map.Map; import org.oscim.map.Map;
import org.oscim.renderer.elements.ElementLayers; import org.oscim.renderer.elements.ElementLayers;
import org.oscim.utils.pool.Inlist; import org.oscim.utils.pool.Inlist;
@ -240,10 +239,6 @@ public class MapRenderer {
} }
} }
public static int depthOffset(MapTile t) {
return ((t.tileX % 4) + (t.tileY % 4 * 4) + 1);
}
public void onSurfaceChanged(int width, int height) { public void onSurfaceChanged(int width, int height) {
log.debug("onSurfaceChanged: new={}, {}x{}", log.debug("onSurfaceChanged: new={}, {}x{}",
mNewSurface, width, height); mNewSurface, width, height);

View File

@ -269,16 +269,18 @@ public class ElementLayers extends TileData {
return layer; return layer;
} }
private final static int[] VERTEX_SHORT_CNT = { public final static int[] VERTEX_SHORT_CNT = {
4, // LINE_VERTEX_SHORTS 4, // LINE_VERTEX
6, // TEXLINE_VERTEX_SHORTS 6, // TEXLINE_VERTEX
2, // POLY_VERTEX_SHORTS 2, // POLY_VERTEX
2, // MESH_VERTEX_SHORTS 2, // MESH_VERTEX
4, // EXTRUSION_VERTEX
}; };
private final static int TEXTURE_VERTEX_SHORTS = 6; private final static int TEXTURE_VERTEX_SHORTS = 6;
private final static int SHORT_BYTES = 2; private final static int SHORT_BYTES = 2;
// TODO move to specific layer implementation
public int getSize() { public int getSize() {
int size = 0; int size = 0;

View File

@ -18,24 +18,23 @@ package org.oscim.renderer.elements;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
import org.oscim.backend.GL20; import org.oscim.backend.canvas.Color;
import org.oscim.backend.GLAdapter;
import org.oscim.core.GeometryBuffer; import org.oscim.core.GeometryBuffer;
import org.oscim.core.GeometryBuffer.GeometryType;
import org.oscim.core.MapElement; import org.oscim.core.MapElement;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.renderer.BufferObject; import org.oscim.renderer.BufferObject;
import org.oscim.renderer.MapRenderer; import org.oscim.renderer.MapRenderer;
import org.oscim.utils.FastMath;
import org.oscim.utils.KeyMap;
import org.oscim.utils.KeyMap.HashItem;
import org.oscim.utils.Tessellator; import org.oscim.utils.Tessellator;
import org.oscim.utils.geom.LineClipper; import org.oscim.utils.geom.LineClipper;
import org.oscim.utils.pool.Inlist; import org.oscim.utils.pool.Inlist;
import org.oscim.utils.pool.SyncPool;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/**
* @author Hannes Janetzek
* FIXME check if polygon has self intersections or 0/180 degree
* angles! or bad things might happen in Triangle
*/
public class ExtrusionLayer extends RenderElement { public class ExtrusionLayer extends RenderElement {
static final Logger log = LoggerFactory.getLogger(ExtrusionLayer.class); static final Logger log = LoggerFactory.getLogger(ExtrusionLayer.class);
@ -46,13 +45,14 @@ public class ExtrusionLayer extends RenderElement {
private final VertexItem mCurIndices[]; private final VertexItem mCurIndices[];
private LineClipper mClipper; private LineClipper mClipper;
/** 16 floats rgba for top, even-side, odd-sides and outline */
public final float[] colors; public final float[] colors;
public final int color;
// indices for: /** indices for: 0. even sides, 1. odd sides, 2. roof, 3. roof outline */
// 0. even sides, 1. odd sides, 2. roof, 3. roof outline public int numIndices[] = { 0, 0, 0, 0, 0 };
public int mIndiceCnt[] = { 0, 0, 0, 0 }; public int sumVertices = 0;
public int mNumIndices = 0; public int sumIndices = 0;
public int mNumVertices = 0;
public BufferObject vboIndices; public BufferObject vboIndices;
public BufferObject vboVertices; public BufferObject vboVertices;
@ -60,65 +60,351 @@ public class ExtrusionLayer extends RenderElement {
//private final static int IND_EVEN_SIDE = 0; //private final static int IND_EVEN_SIDE = 0;
//private final static int IND_ODD_SIDE = 1; //private final static int IND_ODD_SIDE = 1;
private final static int IND_ROOF = 2; private final static int IND_ROOF = 2;
// FIXME flip OUTLINE / MESH!
private final static int IND_OUTLINE = 3; private final static int IND_OUTLINE = 3;
private final static int IND_MESH = 4;
public boolean compiled = false; public boolean compiled = false;
private final float mGroundResolution; private final float mGroundResolution;
private KeyMap<Vertex> mVertexMap;
public int indexOffset;
//private static final int NORMAL_DIR_MASK = 0xFFFFFFFE;
//private int numIndexHits = 0;
/**
* ExtrusionLayer for polygon geometries.
*/
public ExtrusionLayer(int level, float groundResolution, float[] colors) { public ExtrusionLayer(int level, float groundResolution, float[] colors) {
super(RenderElement.EXTRUSION); super(RenderElement.EXTRUSION);
this.level = level; this.level = level;
this.colors = colors; this.colors = colors;
this.color = 0;
mGroundResolution = groundResolution; mGroundResolution = groundResolution;
mVertices = mCurVertices = VertexItem.pool.get(); mVertices = mCurVertices = VertexItem.pool.get();
mIndices = new VertexItem[4]; mIndices = new VertexItem[5];
mCurIndices = new VertexItem[4]; mCurIndices = new VertexItem[5];
for (int i = 0; i < 4; i++) for (int i = 0; i <= IND_MESH; i++)
mIndices[i] = mCurIndices[i] = VertexItem.pool.get(); mIndices[i] = mCurIndices[i] = VertexItem.pool.get();
mClipper = new LineClipper(0, 0, Tile.SIZE, Tile.SIZE); mClipper = new LineClipper(0, 0, Tile.SIZE, Tile.SIZE);
} }
/**
* ExtrusionLayer for triangle geometries.
*/
public ExtrusionLayer(int level, float groundResolution, int color) {
super(RenderElement.EXTRUSION);
this.level = level;
this.color = color;
this.colors = new float[4];
float a = Color.aToFloat(color);
colors[0] = a * Color.rToFloat(color);
colors[1] = a * Color.gToFloat(color);
colors[2] = a * Color.bToFloat(color);
colors[3] = a;
mGroundResolution = groundResolution;
mVertices = mCurVertices = VertexItem.pool.get();
mIndices = new VertexItem[5];
mCurIndices = new VertexItem[5];
mIndices[4] = mCurIndices[4] = VertexItem.pool.get();
mVertexMap = vertexMapPool.get();
}
static SyncPool<Vertex> vertexPool = new SyncPool<Vertex>(8192, false) {
@Override
protected Vertex createItem() {
return new Vertex();
}
};
static SyncPool<KeyMap<Vertex>> vertexMapPool = new SyncPool<KeyMap<Vertex>>(64, false) {
@Override
protected KeyMap<Vertex> createItem() {
return new KeyMap<Vertex>(2048);
}
};
static class Vertex extends HashItem {
short x, y, z, n;
int id;
@Override
public boolean equals(Object obj) {
Vertex o = (Vertex) obj;
return x == o.x && y == o.y && z == o.z && n == o.n;
}
@Override
public int hashCode() {
return 7 + ((x << 16 | y) ^ (n << 16 | z)) * 31;
}
public Vertex set(short x, short y, short z, short n) {
this.x = x;
this.y = y;
this.z = z;
this.n = n;
return this;
}
}
public void add(MapElement element) {
if (element.type != GeometryType.TRIS)
return;
short[] index = element.index;
float[] points = element.points;
int vertexCnt = sumVertices;
Vertex key = vertexPool.get();
double scale = S * Tile.SIZE / 4096;
for (int k = 0, n = index.length; k < n;) {
if (index[k] < 0)
break;
/* FIXME: workaround: dont overflow max index id. */
if (vertexCnt >= 1 << 16)
break;
int vtx1 = index[k++] * 3;
int vtx2 = index[k++] * 3;
int vtx3 = index[k++] * 3;
float vx1 = points[vtx1 + 0];
float vy1 = points[vtx1 + 1];
float vz1 = points[vtx1 + 2];
float vx2 = points[vtx2 + 0];
float vy2 = points[vtx2 + 1];
float vz2 = points[vtx2 + 2];
float vx3 = points[vtx3 + 0];
float vy3 = points[vtx3 + 1];
float vz3 = points[vtx3 + 2];
float ax = vx2 - vx1;
float ay = vy2 - vy1;
float az = vz2 - vz1;
float bx = vx3 - vx1;
float by = vy3 - vy1;
float bz = vz3 - vz1;
float cx = ay * bz - az * by;
float cy = az * bx - ax * bz;
float cz = ax * by - ay * bx;
double len = Math.sqrt(cx * cx + cy * cy + cz * cz);
// packing the normal in two bytes
// int mx = FastMath.clamp(127 + (int) ((cx / len) * 128), 0, 0xff);
// int my = FastMath.clamp(127 + (int) ((cy / len) * 128), 0, 0xff);
// short normal = (short) ((my << 8) | (mx & NORMAL_DIR_MASK) | (cz > 0 ? 1 : 0));
double p = Math.sqrt((cz / len) * 8.0 + 8.0);
int mx = FastMath.clamp(127 + (int) ((cx / len / p) * 128), 0, 255);
int my = FastMath.clamp(127 + (int) ((cy / len / p) * 128), 0, 255);
short normal = (short) ((my << 8) | mx);
if (key == null)
key = vertexPool.get();
key.set((short) (vx1 * scale),
(short) (vy1 * scale),
(short) (vz1 * scale),
normal);
Vertex vertex = mVertexMap.put(key, false);
if (vertex == null) {
key.id = vertexCnt++;
addVertex(key);
addIndex(key);
key = vertexPool.get();
} else {
//numIndexHits++;
addIndex(vertex);
}
key.set((short) (vx2 * scale),
(short) (vy2 * scale),
(short) (vz2 * scale),
normal);
vertex = mVertexMap.put(key, false);
if (vertex == null) {
key.id = vertexCnt++;
addVertex(key);
addIndex(key);
key = vertexPool.get();
} else {
//numIndexHits++;
addIndex(vertex);
}
key.set((short) (vx3 * scale),
(short) (vy3 * scale),
(short) (vz3 * scale),
(short) normal);
vertex = mVertexMap.put(key, false);
if (vertex == null) {
key.id = vertexCnt++;
addVertex(key);
addIndex(key);
key = vertexPool.get();
} else {
//numIndexHits++;
addIndex(vertex);
}
}
vertexPool.release(key);
sumVertices = vertexCnt;
}
private void addVertex(Vertex vertex) {
VertexItem vi = mCurVertices;
if (vi.used == VertexItem.SIZE) {
mCurVertices.used = VertexItem.SIZE;
mCurVertices = VertexItem.pool.getNext(vi);
vi = mCurVertices;
}
vi.vertices[vi.used++] = vertex.x;
vi.vertices[vi.used++] = vertex.y;
vi.vertices[vi.used++] = vertex.z;
vi.vertices[vi.used++] = vertex.n;
}
private void addIndex(Vertex v) {
VertexItem vi = mCurIndices[IND_MESH];
if (vi.used == VertexItem.SIZE) {
mCurIndices[IND_MESH].used = VertexItem.SIZE;
mCurIndices[IND_MESH] = VertexItem.pool.getNext(vi);
vi = mCurIndices[IND_MESH];
}
sumIndices++;
vi.vertices[vi.used++] = (short) v.id;
}
// private void encodeNormal(float v[], int offset) {
// var p = Math.sqrt(cartesian.z * 8.0 + 8.0);
// var result = new Cartesian2();
// result.x = cartesian.x / p + 0.5;
// result.y = cartesian.y / p + 0.5;
// return result;
// }
//
public void addNoNormal(MapElement element) {
if (element.type != GeometryType.TRIS)
return;
short[] index = element.index;
float[] points = element.points;
/* current vertex id */
int startVertex = sumVertices;
/* roof indices for convex shapes */
int i = mCurIndices[IND_MESH].used;
short[] indices = mCurIndices[IND_MESH].vertices;
int first = startVertex;
for (int k = 0, n = index.length; k < n;) {
if (index[k] < 0)
break;
if (i == VertexItem.SIZE) {
mCurIndices[IND_MESH].used = VertexItem.SIZE;
mCurIndices[IND_MESH].next = VertexItem.pool.get();
mCurIndices[IND_MESH] = mCurIndices[IND_MESH].next;
indices = mCurIndices[IND_MESH].vertices;
i = 0;
}
indices[i++] = (short) (first + index[k++]);
indices[i++] = (short) (first + index[k++]);
indices[i++] = (short) (first + index[k++]);
}
mCurIndices[IND_MESH].used = i;
short[] vertices = mCurVertices.vertices;
int v = mCurVertices.used;
int vertexCnt = element.pointPos;
for (int j = 0; j < vertexCnt;) {
/* add bottom and top vertex for each point */
if (v == VertexItem.SIZE) {
mCurVertices.used = VertexItem.SIZE;
mCurVertices.next = VertexItem.pool.get();
mCurVertices = mCurVertices.next;
vertices = mCurVertices.vertices;
v = 0;
}
/* set coordinate */
vertices[v++] = (short) (points[j++] * S);
vertices[v++] = (short) (points[j++] * S);
vertices[v++] = (short) (points[j++] * S);
v++;
}
mCurVertices.used = v;
sumVertices += (vertexCnt / 3);
}
public void add(MapElement element, float height, float minHeight) { public void add(MapElement element, float height, float minHeight) {
short[] index = element.index; short[] index = element.index;
float[] points = element.points; float[] points = element.points;
// 10 cm steps /* 10 cm steps */
float sfactor = 1 / 10f; float sfactor = 1 / 10f;
height *= sfactor; height *= sfactor;
minHeight *= sfactor; minHeight *= sfactor;
// match height with ground resultion /* match height with ground resultion (meter per pixel) */
// (meter per pixel)
height /= mGroundResolution; height /= mGroundResolution;
minHeight /= mGroundResolution; minHeight /= mGroundResolution;
boolean complexOutline = false; boolean complexOutline = false;
boolean simpleOutline = true; boolean simpleOutline = true;
// current vertex id /* current vertex id */
int startVertex = mNumVertices; int startVertex = sumVertices;
int length = 0, ipos = 0, ppos = 0; int length = 0, ipos = 0, ppos = 0;
for (int n = index.length; ipos < n; ipos++, ppos += length) { for (int n = index.length; ipos < n; ipos++, ppos += length) {
length = index[ipos]; length = index[ipos];
// end marker /* end marker */
if (length < 0) if (length < 0)
break; break;
// start next polygon /* start next polygon */
if (length == 0) { if (length == 0) {
startVertex = mNumVertices; startVertex = sumVertices;
simpleOutline = true; simpleOutline = true;
complexOutline = false; complexOutline = false;
continue; continue;
} }
// check: drop last point from explicitly closed rings /* check: drop last point from explicitly closed rings */
int len = length; int len = length;
if (points[ppos] == points[ppos + len - 2] if (points[ppos] == points[ppos + len - 2]
&& points[ppos + 1] == points[ppos + len - 1]) { && points[ppos + 1] == points[ppos + len - 1]) {
@ -126,11 +412,11 @@ public class ExtrusionLayer extends RenderElement {
log.debug("explicit closed poly " + len); log.debug("explicit closed poly " + len);
} }
// need at least three points /* need at least three points */
if (len < 6) if (len < 6)
continue; continue;
// check if polygon contains inner rings /* check if polygon contains inner rings */
if (simpleOutline && (ipos < n - 1) && (index[ipos + 1] > 0)) if (simpleOutline && (ipos < n - 1) && (index[ipos + 1] > 0))
simpleOutline = false; simpleOutline = false;
@ -147,7 +433,7 @@ public class ExtrusionLayer extends RenderElement {
} }
private void addRoofSimple(int startVertex, int len) { private void addRoofSimple(int startVertex, int len) {
// roof indices for convex shapes /* roof indices for convex shapes */
int i = mCurIndices[IND_ROOF].used; int i = mCurIndices[IND_ROOF].used;
short[] indices = mCurIndices[IND_ROOF].vertices; short[] indices = mCurIndices[IND_ROOF].vertices;
short first = (short) (startVertex + 1); short first = (short) (startVertex + 1);
@ -156,13 +442,15 @@ public class ExtrusionLayer extends RenderElement {
if (i == VertexItem.SIZE) { if (i == VertexItem.SIZE) {
mCurIndices[IND_ROOF].used = VertexItem.SIZE; mCurIndices[IND_ROOF].used = VertexItem.SIZE;
mCurIndices[IND_ROOF].next = VertexItem.pool.get(); mCurIndices[IND_ROOF].next = VertexItem.pool.get();
mCurIndices[IND_ROOF] = mCurIndices[2].next; mCurIndices[IND_ROOF] = mCurIndices[IND_ROOF].next;
indices = mCurIndices[IND_ROOF].vertices; indices = mCurIndices[IND_ROOF].vertices;
i = 0; i = 0;
} }
indices[i++] = first; indices[i++] = first;
indices[i++] = (short) (first + k + 2); indices[i++] = (short) (first + k + 2);
indices[i++] = (short) (first + k + 4); indices[i++] = (short) (first + k + 4);
sumIndices += 3;
} }
mCurIndices[IND_ROOF].used = i; mCurIndices[IND_ROOF].used = i;
} }
@ -174,14 +462,14 @@ public class ExtrusionLayer extends RenderElement {
int len = 0; int len = 0;
int rings = 0; int rings = 0;
// get sum of points in polygon /* get sum of points in polygon */
for (int i = ipos, n = index.length; i < n && index[i] > 0; i++) { for (int i = ipos, n = index.length; i < n && index[i] > 0; i++) {
len += index[i]; len += index[i];
rings++; rings++;
} }
Tessellator.tessellate(points, ppos, len, index, ipos, rings, sumIndices += Tessellator.tessellate(points, ppos, len, index, ipos, rings,
startVertex + 1, mCurIndices[IND_ROOF]); startVertex + 1, mCurIndices[IND_ROOF]);
mCurIndices[IND_ROOF] = Inlist.last(mCurIndices[IND_ROOF]); mCurIndices[IND_ROOF] = Inlist.last(mCurIndices[IND_ROOF]);
} }
@ -189,7 +477,7 @@ public class ExtrusionLayer extends RenderElement {
private boolean addOutline(float[] points, int pos, int len, float minHeight, private boolean addOutline(float[] points, int pos, int len, float minHeight,
float height, boolean convex) { float height, boolean convex) {
// add two vertices for last face to make zigzag indices work /* add two vertices for last face to make zigzag indices work */
boolean addFace = (len % 4 != 0); boolean addFace = (len % 4 != 0);
int vertexCnt = len + (addFace ? 2 : 0); int vertexCnt = len + (addFace ? 2 : 0);
@ -201,10 +489,10 @@ public class ExtrusionLayer extends RenderElement {
float nx = points[pos + 0]; float nx = points[pos + 0];
float ny = points[pos + 1]; float ny = points[pos + 1];
// vector to next point /* vector to next point */
float vx = nx - cx; float vx = nx - cx;
float vy = ny - cy; float vy = ny - cy;
// vector from previous point /* vector from previous point */
float ux, uy; float ux, uy;
float a = (float) Math.sqrt(vx * vx + vy * vy); float a = (float) Math.sqrt(vx * vx + vy * vy);
@ -217,8 +505,8 @@ public class ExtrusionLayer extends RenderElement {
int changeY = 0; int changeY = 0;
int angleSign = 0; int angleSign = 0;
// vertex offset for all vertices in layer /* vertex offset for all vertices in layer */
int vOffset = mNumVertices; int vOffset = sumVertices;
short[] vertices = mCurVertices.vertices; short[] vertices = mCurVertices.vertices;
int v = mCurVertices.used; int v = mCurVertices.used;
@ -241,15 +529,15 @@ public class ExtrusionLayer extends RenderElement {
v = 0; v = 0;
} }
// set coordinate /* set coordinate */
vertices[v + 0] = vertices[v + 4] = (short) (cx * S); vertices[v + 0] = vertices[v + 4] = (short) (cx * S);
vertices[v + 1] = vertices[v + 5] = (short) (cy * S); vertices[v + 1] = vertices[v + 5] = (short) (cy * S);
// set height /* set height */
vertices[v + 2] = mh; vertices[v + 2] = mh;
vertices[v + 6] = h; vertices[v + 6] = h;
// get direction to next point /* get direction to next point */
if (i < len) { if (i < len) {
nx = points[pos + i + 0]; nx = points[pos + i + 0];
ny = points[pos + i + 1]; ny = points[pos + i + 1];
@ -266,7 +554,7 @@ public class ExtrusionLayer extends RenderElement {
vx = nx - cx; vx = nx - cx;
vy = ny - cy; vy = ny - cy;
// set lighting (by direction) /* set lighting (by direction) */
a = (float) Math.sqrt(vx * vx + vy * vy); a = (float) Math.sqrt(vx * vx + vy * vy);
color2 = (short) ((1 + vx / a) * 127); color2 = (short) ((1 + vx / a) * 127);
@ -281,8 +569,8 @@ public class ExtrusionLayer extends RenderElement {
/* check if polygon is convex */ /* check if polygon is convex */
if (convex) { if (convex) {
// TODO simple polys with only one concave arc /* TODO simple polys with only one concave arc
// could be handled without special triangulation * could be handled without special triangulation */
if ((ux < 0 ? 1 : -1) != (vx < 0 ? 1 : -1)) if ((ux < 0 ? 1 : -1) != (vx < 0 ? 1 : -1))
changeX++; changeX++;
if ((uy < 0 ? 1 : -1) != (vy < 0 ? 1 : -1)) if ((uy < 0 ? 1 : -1) != (vy < 0 ? 1 : -1))
@ -306,7 +594,7 @@ public class ExtrusionLayer extends RenderElement {
/* check if face is within tile */ /* check if face is within tile */
if (mClipper.clipNext((int) nx, (int) ny) == 0) { if (mClipper.clipNext((int) nx, (int) ny) == 0) {
even = (even == 0 ? 1 : 0); even = ++even % 2;
continue; continue;
} }
@ -317,99 +605,112 @@ public class ExtrusionLayer extends RenderElement {
short s2 = vert++; short s2 = vert++;
short s3 = vert++; short s3 = vert++;
// connect last to first (when number of faces is even) /* connect last to first (when number of faces is even) */
if (!addFace && i == len) { if (!addFace && i == len) {
s2 -= len; s2 -= len;
s3 -= len; s3 -= len;
} }
short[] indices = mCurIndices[even].vertices; VertexItem it = mCurIndices[even];
// index id relative to mCurIndices item if (it.used == VertexItem.SIZE) {
int ind = mCurIndices[even].used; it = VertexItem.pool.getNext(it);
mCurIndices[even] = it;
if (ind == VertexItem.SIZE) {
mCurIndices[even].next = VertexItem.pool.get();
mCurIndices[even] = mCurIndices[even].next;
indices = mCurIndices[even].vertices;
ind = 0;
} }
int ind = it.used;
short[] indices = it.vertices;
indices[ind + 0] = s0; indices[ind + 0] = s0;
indices[ind + 1] = s2; indices[ind + 1] = s2;
indices[ind + 2] = s1; indices[ind + 2] = s1;
indices[ind + 3] = s1; indices[ind + 3] = s1;
indices[ind + 4] = s2; indices[ind + 4] = s2;
indices[ind + 5] = s3; indices[ind + 5] = s3;
it.used += 6;
sumIndices += 6;
mCurIndices[even].used += 6; /* flipp even-odd */
even = (even == 0 ? 1 : 0); even = ++even % 2;
/* add roof outline indices */ /* add roof outline indices */
VertexItem it = mCurIndices[IND_OUTLINE]; it = mCurIndices[IND_OUTLINE];
if (it.used == VertexItem.SIZE) { if (it.used == VertexItem.SIZE) {
it.next = VertexItem.pool.get(); it = VertexItem.pool.getNext(it);
it = mCurIndices[IND_OUTLINE] = it.next; mCurIndices[IND_OUTLINE] = it;
} }
it.vertices[it.used++] = s1; ind = it.used;
it.vertices[it.used++] = s3; indices = it.vertices;
indices[ind + 0] = s1;
indices[ind + 1] = s3;
it.used += 2;
sumIndices += 2;
} }
mCurVertices.used = v; mCurVertices.used = v;
mNumVertices += vertexCnt; sumVertices += vertexCnt;
return convex; return convex;
} }
@Override @Override
public void compile(ShortBuffer sbuf) { public void compile(ShortBuffer vertexBuffer, ShortBuffer indexBuffer) {
mClipper = null;
if (mNumVertices == 0 || compiled) if (compiled) {
return; throw new IllegalStateException();
mNumIndices = 0;
for (int i = 0; i < 4; i++) {
for (VertexItem vi = mIndices[i]; vi != null; vi = vi.next) {
sbuf.put(vi.vertices, 0, vi.used);
mIndiceCnt[i] += vi.used;
}
mNumIndices += mIndiceCnt[i];
} }
int size = mNumIndices * 2; if (mVertexMap != null) {
vboIndices = BufferObject.get(GL20.GL_ELEMENT_ARRAY_BUFFER, size); vertexPool.releaseAll(mVertexMap.releaseItems());
vboIndices.loadBufferData(sbuf.flip(), size); mVertexMap = vertexMapPool.release(mVertexMap);
mVertexMap = null;
}
GL20 GL = GLAdapter.get(); if (sumVertices == 0) {
GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0); compiled = true;
return;
}
indexOffset = indexBuffer.position();
for (int i = 0; i <= IND_MESH; i++) {
for (VertexItem vi = mIndices[i]; vi != null; vi = vi.next) {
indexBuffer.put(vi.vertices, 0, vi.used);
numIndices[i] += vi.used;
}
}
//log.debug("INDEX HITS " + numIndexHits + " / " + sumVertices + " / " + sumIndices);
offset = vertexBuffer.position() * 2;
// upload vertices
sbuf.clear();
for (VertexItem vi = mVertices; vi != null; vi = vi.next) for (VertexItem vi = mVertices; vi != null; vi = vi.next)
sbuf.put(vi.vertices, 0, vi.used); vertexBuffer.put(vi.vertices, 0, vi.used);
size = mNumVertices * 4 * 2;
vboVertices = BufferObject.get(GL20.GL_ARRAY_BUFFER, size);
vboVertices.loadBufferData(sbuf.flip(), size);
GL.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);
clear(); clear();
compiled = true; compiled = true;
mClipper = null;
} }
@Override @Override
protected void clear() { protected void clear() {
mClipper = null;
if (mVertexMap != null) {
vertexPool.releaseAll(mVertexMap.releaseItems());
mVertexMap = vertexMapPool.release(mVertexMap);
mVertexMap = null;
}
if (compiled) { if (compiled) {
vboIndices = BufferObject.release(vboIndices); vboIndices = BufferObject.release(vboIndices);
vboVertices = BufferObject.release(vboVertices); vboVertices = BufferObject.release(vboVertices);
} else { } else {
for (int i = 0; i < 4; i++) for (int i = 0; i <= IND_MESH; i++)
mIndices[i] = VertexItem.pool.releaseAll(mIndices[i]); mIndices[i] = VertexItem.pool.releaseAll(mIndices[i]);
mIndices = null; mIndices = null;
mVertices = VertexItem.pool.releaseAll(mVertices); mVertices = VertexItem.pool.releaseAll(mVertices);
} }
} }
public ExtrusionLayer next() {
return (ExtrusionLayer) next;
}
} }

View File

@ -28,9 +28,9 @@ public abstract class RenderElement extends Inlist<RenderElement> {
public final static int TEXLINE = 1; public final static int TEXLINE = 1;
public final static int POLYGON = 2; public final static int POLYGON = 2;
public final static int MESH = 3; public final static int MESH = 3;
public final static int SYMBOL = 4; public final static int EXTRUSION = 4;
public final static int BITMAP = 5; public final static int SYMBOL = 5;
public final static int EXTRUSION = 6; public final static int BITMAP = 6;
public final int type; public final int type;
@ -60,12 +60,12 @@ public abstract class RenderElement extends Inlist<RenderElement> {
} }
public int getOffset() { public int getOffset() {
return offset; return offset;
} }
public void setOffset(int offset) { public void setOffset(int offset) {
this.offset = offset; this.offset = offset;
} }
/** /**
* For line- and polygon-layers this is the offset * For line- and polygon-layers this is the offset
@ -73,4 +73,8 @@ public abstract class RenderElement extends Inlist<RenderElement> {
* For all other types it is the byte offset in vbo. * For all other types it is the byte offset in vbo.
*/ */
protected int offset; protected int offset;
protected void compile(ShortBuffer vertexBuffer, ShortBuffer indexBuffer) {
}
} }

View File

@ -1,143 +0,0 @@
/*
* Copyright 2013 Hannes Janetzek
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.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.renderer.test;
import java.util.Arrays;
import org.oscim.backend.canvas.Color;
import org.oscim.backend.canvas.Paint.Cap;
import org.oscim.renderer.ElementRenderer;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.atlas.TextureAtlas;
import org.oscim.renderer.atlas.TextureAtlas.Rect;
import org.oscim.renderer.atlas.TextureAtlas.Slot;
import org.oscim.renderer.elements.LineLayer;
import org.oscim.renderer.elements.TextItem;
import org.oscim.renderer.elements.TextLayer;
import org.oscim.theme.styles.LineStyle;
import org.oscim.theme.styles.TextStyle;
import org.oscim.theme.styles.TextStyle.TextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AtlasRenderLayer extends ElementRenderer {
Logger log = LoggerFactory.getLogger(AtlasRenderLayer.class);
public AtlasRenderLayer() {
TextureAtlas mAtlas = TextureAtlas.create(2048, 2048, 1);
LineLayer ll = layers.getLineLayer(0);
ll.line = new LineStyle(Color.BLUE, 3, Cap.BUTT);
ll.scale = 1f;
LineLayer ll2 = layers.getLineLayer(1);
ll2.line = new LineStyle(Color.RED, 3, Cap.BUTT);
ll2.scale = 1f;
LineLayer ll3 = layers.getLineLayer(2);
ll3.line = new LineStyle(Color.GREEN, 3, Cap.BUTT);
ll3.scale = 1f;
TextLayer tl = new TextLayer();
TextStyle t = new TextBuilder().setFontSize(20).setColor(Color.BLACK).build();
layers.setTextureLayers(tl);
float[] points = new float[10];
for (int i = 0; i < 400; i++) {
int w = (int) (20 + Math.random() * 256);
int h = (int) (20 + Math.random() * 56);
Rect r = mAtlas.getRegion(w, h);
if (r == null) {
log.debug("no space left");
continue;
}
r.x += 1;
r.y += 1;
points[0] = r.x;
points[1] = r.y;
points[2] = r.x + (r.w - 2);
points[3] = r.y;
points[4] = r.x + (r.w - 2);
points[5] = r.y + (r.h - 2);
points[6] = r.x;
points[7] = r.y + (r.h - 2);
points[8] = r.x;
points[9] = r.y;
ll.addLine(points, 10, false);
r.x += 1;
r.y += 1;
points[0] = r.x;
points[1] = r.y;
points[2] = r.x + (w - 4);
points[3] = r.y;
points[4] = r.x + (w - 4);
points[5] = r.y + (h - 4);
points[6] = r.x;
points[7] = r.y + (h - 4);
points[8] = r.x;
points[9] = r.y;
log.debug("add region: " + Arrays.toString(points));
ll2.addLine(points, 10, false);
TextItem ti = TextItem.pool.get();
ti.set(r.x + r.w / 2, r.y + r.h / 2, "" + i, t);
ti.x1 = 0;
ti.y1 = 1; // (short) (size / 2);
ti.x2 = 1; // (short) size;
ti.y2 = 1;
tl.addText(ti);
}
for (Slot s = mAtlas.mSlots; s != null; s = s.next) {
points[0] = s.x;
points[1] = s.y;
points[2] = s.x + s.w;
points[3] = s.y;
points[4] = s.x + s.w;
points[5] = 2048;
points[6] = s.x;
points[7] = 2048;
points[8] = s.x;
points[9] = s.y;
ll3.addLine(points, 10, false);
}
tl.prepare();
tl.labels = TextItem.pool.releaseAll(tl.labels);
}
boolean initial = true;
@Override
protected void update(GLViewport v) {
if (initial) {
mMapPosition.copy(v.pos);
initial = false;
compile();
}
}
}

View File

@ -1,72 +0,0 @@
package org.oscim.renderer.test;
import java.util.List;
import org.oscim.backend.canvas.Color;
import org.oscim.core.GeometryBuffer;
import org.oscim.core.Point;
import org.oscim.renderer.ElementRenderer;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.elements.LineLayer;
import org.oscim.theme.styles.LineStyle;
import org.oscim.utils.geom.BezierPath;
public class BezierPathLayer extends ElementRenderer {
public BezierPathLayer() {
mMapPosition.scale = 0;
GeometryBuffer g = new GeometryBuffer(100, 1);
g.startLine();
Point[] pts = new Point[10];
for (int i = 0; i < 10; i++) {
pts[i] = new Point(i * 3, (i * i) % 3 * 4);
pts[i].x *= 10;
pts[i].y *= 10;
// System.out.println(pts[i]);
g.addPoint(pts[i]);
}
LineLayer ll = layers.addLineLayer(0, new LineStyle(Color.BLUE, 2f));
ll.addLine(g);
List<Point> ctrl = BezierPath.cubicSplineControlPoints(pts, 0.1f);
g.clear();
g.startLine();
Point p0 = pts[0];
for (int j = 1, k = 0; j < pts.length; j++) {
Point p1 = ctrl.get(k++);
Point p2 = ctrl.get(k++);
Point p3 = pts[j];
System.out.println(">>> " + p1 + " " + p2);
for (int i = 0; i < 10; i++) {
double mu = (i / 10f);
Point p = BezierPath.cubicBezier(p0, p1, p2, p3, mu);
g.addPoint(p);
System.out.println(mu + " " + p);
}
p0 = p3;
}
ll = layers.addLineLayer(1, new LineStyle(Color.CYAN, 2f));
ll.addLine(g);
}
public synchronized void clear() {
layers.clear();
setReady(false);
}
@Override
protected synchronized void update(GLViewport v) {
if (mMapPosition.scale == 0)
mMapPosition.copy(v.pos);
if (!isReady()) {
compile();
}
}
}

View File

@ -1,163 +0,0 @@
/*
* Copyright 2013 Hannes Janetzek
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.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.renderer.test;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import org.oscim.backend.GL20;
import org.oscim.core.MapPosition;
import org.oscim.map.Map;
import org.oscim.renderer.GLState;
import org.oscim.renderer.GLUtils;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.LayerRenderer;
/*
* This is an example how to integrate custom OpenGL drawing routines as map overlay
*
* based on chapter 2 from:
* https://github.com/dalinaum/opengl-es-book-samples/tree/master/Android
* */
public class CustomRenderer extends LayerRenderer {
private final Map mMap;
private final MapPosition mMapPosition;
private int mProgramObject;
private int hVertexPosition;
private int hMatrixPosition;
private FloatBuffer mVertices;
private final float[] mVerticesData = {
-200, -200, 1.0f,
200, 200, 0,
-200, 200, 0.5f,
200, -200, 0.5f,
};
private boolean mInitialized;
public CustomRenderer(Map map) {
mMap = map;
mMapPosition = new MapPosition();
}
// ---------- everything below runs in GLRender Thread ----------
@Override
protected void update(GLViewport v) {
if (!mInitialized) {
if (!init())
return;
mInitialized = true;
// fix current MapPosition
mMapPosition.copy(v.pos);
compile();
}
}
protected void compile() {
// modify mVerticesData and put in FloatBuffer
mVertices.clear();
mVertices.put(mVerticesData);
mVertices.flip();
setReady(true);
}
@Override
protected void render(GLViewport v) {
// Use the program object
GLState.useProgram(mProgramObject);
GLState.blend(true);
GLState.test(false, false);
// unbind previously bound VBOs
GL.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);
// Load the vertex data
//mVertices.position(0);
GL.glVertexAttribPointer(hVertexPosition, 3, GL20.GL_FLOAT, false, 0, mVertices);
//mVertices.position(2);
//GL.glVertexAttribPointer(hVertexPosition, 2, GL20.GL_FLOAT, false, 4, mVertices);
GLState.enableVertexArrays(hVertexPosition, -1);
/* apply view and projection matrices */
// set mvp (tmp) matrix relative to mMapPosition
// i.e. fixed on the map
float ratio = 1f / mMap.getWidth();
v.mvp.setScale(ratio, ratio, 1);
v.mvp.multiplyLhs(v.proj);
v.mvp.setAsUniform(hMatrixPosition);
// Draw the triangle
GL.glDrawArrays(GL20.GL_TRIANGLE_STRIP, 0, 4);
GLUtils.checkGlError("...");
}
private boolean init() {
// Load the vertex/fragment shaders
int programObject = GLUtils.createProgram(vShaderStr, fShaderStr);
if (programObject == 0)
return false;
// Handle for vertex position in shader
hVertexPosition = GL.glGetAttribLocation(programObject, "a_pos");
hMatrixPosition = GL.glGetUniformLocation(programObject, "u_mvp");
// Store the program object
mProgramObject = programObject;
mVertices = ByteBuffer.allocateDirect(mVerticesData.length * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
return true;
}
private final static String vShaderStr = "" +
"precision mediump float;"
+ "uniform mat4 u_mvp;"
+ "attribute vec4 a_pos;"
+ "varying float alpha;"
+ "void main()"
+ "{"
+ " gl_Position = u_mvp * vec4(a_pos.xy, 0.0, 1.0);"
+ " alpha = a_pos.z;"
+ "}";
private final static String fShaderStr = "" +
"precision mediump float;"
+ "varying float alpha;"
+ "void main()"
+ "{"
+ " gl_FragColor = vec4 (alpha, 1.0-alpha, 0.0, 0.7 );"
+ "}";
}

View File

@ -1,207 +0,0 @@
/*
* Copyright 2013 Hannes Janetzek
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.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.renderer.test;
import java.nio.FloatBuffer;
import org.oscim.backend.GL20;
import org.oscim.backend.canvas.Color;
import org.oscim.renderer.BufferObject;
import org.oscim.renderer.ElementRenderer;
import org.oscim.renderer.GLState;
import org.oscim.renderer.GLUtils;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.MapRenderer;
import org.oscim.utils.FastMath;
/*
* This is an example how to integrate custom OpenGL drawing routines as map overlay
*
* based on chapter 2 from:
* https://github.com/dalinaum/opengl-es-book-samples/tree/master/Android
* */
public class CustomRenderer2 extends ElementRenderer {
private int mProgramObject;
private int hVertexPosition;
private int hMatrixPosition;
private int hColorPosition;
private int hCenterPosition;
//private FloatBuffer mVertices;
private boolean mInitialized;
private BufferObject mVBO;
int mZoom = -1;
float mCellScale = 60 * MapRenderer.COORD_SCALE;
@Override
protected void update(GLViewport v) {
if (!mInitialized) {
if (!init())
return;
mInitialized = true;
compile();
// fix current MapPosition
//mMapPosition.setPosition(53.1, 8.8);
//mMapPosition.setZoomLevel(14);
}
if (mZoom != v.pos.zoomLevel) {
mMapPosition.copy(v.pos);
mZoom = v.pos.zoomLevel;
}
}
@Override
protected void compile() {
float[] vertices = new float[12];
for (int i = 0; i < 6; i++) {
vertices[i * 2 + 0] = (float) Math.cos(Math.PI * 2 * i / 6) * mCellScale;
vertices[i * 2 + 1] = (float) Math.sin(Math.PI * 2 * i / 6) * mCellScale;
}
FloatBuffer buf = MapRenderer.getFloatBuffer(12);
buf.put(vertices);
mVBO = BufferObject.get(GL20.GL_ARRAY_BUFFER, 0);
mVBO.loadBufferData(buf.flip(), 12 * 4);
setReady(true);
}
@Override
protected void render(GLViewport v) {
// Use the program object
GLState.useProgram(mProgramObject);
GLState.blend(true);
GLState.test(false, false);
// bind VBO data
mVBO.bind();
// set VBO vertex layout
GL.glVertexAttribPointer(hVertexPosition, 2, GL20.GL_FLOAT, false, 0, 0);
GLState.enableVertexArrays(hVertexPosition, -1);
/* apply view and projection matrices */
// set mvp (tmp) matrix relative to mMapPosition
// i.e. fixed on the map
setMatrix(v);
v.mvp.setAsUniform(hMatrixPosition);
final int offset_x = 4;
final int offset_y = 16;
float h = (float) (Math.sqrt(3) / 2);
for (int y = -offset_y; y < offset_y; y++) {
for (int x = -offset_x; x < offset_x; x++) {
float xx = x * 2 + (y % 2 == 0 ? 1 : 0);
float yy = y * h + h / 2;
GL.glUniform2f(hCenterPosition, xx * (mCellScale * 1.5f), yy * mCellScale);
//float alpha = 1 + (float) Math.log10(FastMath.clamp(
// (float) Math.sqrt(xx * xx + yy * yy) / offset_y, 0.0f, 1.0f)) * 2;
float alpha = (float) Math.sqrt(xx * xx + yy * yy) / offset_y;
float fy = (float) (y + offset_y) / (offset_y * 2);
float fx = (float) (x + offset_x) / (offset_x * 2);
float fz = FastMath.clamp(
(float) (x < 0 || y < 0 ? 1 - Math.sqrt(fx * fx + fy * fy)
: 0),
0,
1);
int c = 0xff << 24
| (int) (0xff * fy) << 16
| (int) (0xff * fx) << 8
| (int) (0xff * fz);
GLUtils.setColor(hColorPosition, c, alpha);
GL.glDrawArrays(GL20.GL_TRIANGLE_FAN, 0, 6);
}
}
GLUtils.setColor(hColorPosition, Color.DKGRAY, 0.3f);
for (int y = -offset_y; y < offset_y; y++) {
for (int x = -offset_x; x < offset_x; x++) {
float xx = x * 2 + (y % 2 == 0 ? 1 : 0);
float yy = y * h + h / 2;
GL.glUniform2f(hCenterPosition, xx * (mCellScale * 1.5f), yy * mCellScale);
GL.glDrawArrays(GL20.GL_LINE_LOOP, 0, 6);
}
}
GLUtils.checkGlError("...");
}
private boolean init() {
// Load the vertex/fragment shaders
int programObject = GLUtils.createProgram(vShaderStr, fShaderStr);
if (programObject == 0)
return false;
// Handle for vertex position in shader
hVertexPosition = GL.glGetAttribLocation(programObject, "a_pos");
hMatrixPosition = GL.glGetUniformLocation(programObject, "u_mvp");
hColorPosition = GL.glGetUniformLocation(programObject, "u_color");
hCenterPosition = GL.glGetUniformLocation(programObject, "u_center");
// Store the program object
mProgramObject = programObject;
return true;
}
private final static String vShaderStr = "" +
"precision mediump float;"
+ "uniform mat4 u_mvp;"
+ "uniform vec2 u_center;"
+ "attribute vec2 a_pos;"
+ "void main()"
+ "{"
+ " gl_Position = u_mvp * vec4(u_center + a_pos, 0.0, 1.0);"
+ "}";
private final static String fShaderStr = "" +
"precision mediump float;"
+ "varying float alpha;"
+ "uniform vec4 u_color;"
+ "void main()"
+ "{"
+ " gl_FragColor = u_color;"
+ "}";
}

View File

@ -1,52 +0,0 @@
/*
* Copyright 2013 Hannes Janetzek
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.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.renderer.test;
import org.oscim.backend.CanvasAdapter;
import org.oscim.renderer.ElementRenderer;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.elements.SymbolItem;
import org.oscim.renderer.elements.SymbolLayer;
public class SymbolRenderLayer extends ElementRenderer {
boolean initialize = true;
public SymbolRenderLayer() {
SymbolLayer l = new SymbolLayer();
layers.setTextureLayers(l);
SymbolItem it = SymbolItem.pool.get();
it.billboard = false;
try {
it.bitmap = CanvasAdapter.g.loadBitmapAsset("jar:symbols/cafe.png");
} catch (Exception e) {
e.printStackTrace();
}
l.addSymbol(it);
}
@Override
protected void update(GLViewport v) {
if (initialize) {
initialize = false;
mMapPosition.copy(v.pos);
compile();
}
}
}

View File

@ -250,6 +250,71 @@ public abstract class PbfDecoder implements ITileDecoder {
return (cnt >> 1); return (cnt >> 1);
} }
protected int decodeInterleavedPoints3D(float[] coords, float scale)
throws IOException {
int bytes = decodeVarint32();
fillBuffer(bytes);
int cnt = 0;
int lastX = 0;
int lastY = 0;
int lastZ = 0;
int cur = 0;
byte[] buf = buffer;
int pos = bufferPos;
int end = pos + bytes;
while (pos < end) {
byte b = buf[pos++];
int val = b;
if (b < 0) {
b = buf[pos++];
val = (val & M1) | (b << S1);
if (b < 0) {
b = buf[pos++];
val = (val & M2) | (b << S2);
if (b < 0) {
b = buf[pos++];
val = (val & M3) | (b << S3);
if (b < 0) {
b = buf[pos++];
val = (val & M4) | (b << S4);
if (b < 0)
throw INVALID_VARINT;
}
}
}
}
// zigzag decoding
int s = ((val >>> 1) ^ -(val & 1));
if (cur == 0) {
lastX = lastX + s;
coords[cnt++] = lastX / scale;
} else if (cur == 1) {
lastY = lastY + s;
coords[cnt++] = lastY / scale;
} else {
lastZ = lastZ + s;
coords[cnt++] = lastZ / scale;
}
cur = (cur + 1) % 3;
}
if (pos != bufferPos + bytes)
throw INVALID_PACKED_SIZE;
bufferPos = pos;
// return number of points read
//FIXME inconsitent with 3d version!
return cnt;
}
protected static int deZigZag(int val) { protected static int deZigZag(int val) {
return ((val >>> 1) ^ -(val & 1)); return ((val >>> 1) ^ -(val & 1));
} }

View File

@ -18,7 +18,6 @@ package org.oscim.tiling.source.oscimap4;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays;
import org.oscim.core.GeometryBuffer.GeometryType; import org.oscim.core.GeometryBuffer.GeometryType;
import org.oscim.core.MapElement; import org.oscim.core.MapElement;
@ -48,10 +47,13 @@ public class TileDecoder extends PbfDecoder {
private static final int TAG_TILE_LINE = 21; private static final int TAG_TILE_LINE = 21;
private static final int TAG_TILE_POLY = 22; private static final int TAG_TILE_POLY = 22;
private static final int TAG_TILE_POINT = 23; private static final int TAG_TILE_POINT = 23;
/** since version 5 */
private static final int TAG_TILE_MESH = 24;
private static final int TAG_ELEM_NUM_INDICES = 1; private static final int TAG_ELEM_NUM_INDICES = 1;
private static final int TAG_ELEM_NUM_TAGS = 2; private static final int TAG_ELEM_NUM_TAGS = 2;
//private static final int TAG_ELEM_HAS_ELEVATION = 3; /** since version 5 */
private static final int TAG_ELEM_NUM_COORDINATES = 3;
private static final int TAG_ELEM_TAGS = 11; private static final int TAG_ELEM_TAGS = 11;
private static final int TAG_ELEM_INDEX = 12; private static final int TAG_ELEM_INDEX = 12;
private static final int TAG_ELEM_COORDS = 13; private static final int TAG_ELEM_COORDS = 13;
@ -66,6 +68,8 @@ public class TileDecoder extends PbfDecoder {
private final TagSet mTileTags; private final TagSet mTileTags;
private ITileDataSink mMapDataSink; private ITileDataSink mMapDataSink;
private int mVersion;
// scale coordinates to tile size // scale coordinates to tile size
private final static float REF_TILE_SIZE = 4096.0f; private final static float REF_TILE_SIZE = 4096.0f;
private final float mScaleFactor = REF_TILE_SIZE / Tile.SIZE; private final float mScaleFactor = REF_TILE_SIZE / Tile.SIZE;
@ -86,7 +90,6 @@ public class TileDecoder extends PbfDecoder {
mMapDataSink = sink; mMapDataSink = sink;
mTileTags.clearAndNullTags(); mTileTags.clearAndNullTags();
int version = -1;
int val; int val;
int numTags = 0; int numTags = 0;
@ -108,6 +111,7 @@ public class TileDecoder extends PbfDecoder {
case TAG_TILE_LINE: case TAG_TILE_LINE:
case TAG_TILE_POLY: case TAG_TILE_POLY:
case TAG_TILE_POINT: case TAG_TILE_POINT:
case TAG_TILE_MESH:
decodeTileElement(tag); decodeTileElement(tag);
break; break;
@ -159,8 +163,8 @@ public class TileDecoder extends PbfDecoder {
break; break;
case TAG_TILE_VERSION: case TAG_TILE_VERSION:
version = decodeVarint32(); int version = decodeVarint32();
if (version != 4) { if (version < 4 || mVersion > 5) {
log.debug("{} invalid version:{}", log.debug("{} invalid version:{}",
mTile, version); mTile, version);
return false; return false;
@ -226,18 +230,19 @@ public class TileDecoder extends PbfDecoder {
return true; return true;
} }
private int decodeWayIndices(int indexCnt) throws IOException { private int decodeWayIndices(int indexCnt, boolean shift) throws IOException {
mElem.ensureIndexSize(indexCnt, false); mElem.ensureIndexSize(indexCnt, false);
decodeVarintArray(indexCnt, mElem.index); decodeVarintArray(indexCnt, mElem.index);
short[] index = mElem.index; short[] index = mElem.index;
int coordCnt = 0; int coordCnt = 0;
for (int i = 0; i < indexCnt; i++) { if (shift) {
coordCnt += index[i]; for (int i = 0; i < indexCnt; i++) {
index[i] *= 2; coordCnt += index[i];
index[i] *= 2;
}
} }
// set end marker // set end marker
if (indexCnt < index.length) if (indexCnt < index.length)
index[indexCnt] = -1; index[indexCnt] = -1;
@ -248,7 +253,6 @@ public class TileDecoder extends PbfDecoder {
private boolean decodeTileElement(int type) throws IOException { private boolean decodeTileElement(int type) throws IOException {
int bytes = decodeVarint32(); int bytes = decodeVarint32();
short[] index = null;
int end = position() + bytes; int end = position() + bytes;
int numIndices = 1; int numIndices = 1;
@ -287,8 +291,17 @@ public class TileDecoder extends PbfDecoder {
numTags = decodeVarint32(); numTags = decodeVarint32();
break; break;
case TAG_ELEM_NUM_COORDINATES:
coordCnt = decodeVarint32();
break;
case TAG_ELEM_INDEX: case TAG_ELEM_INDEX:
coordCnt = decodeWayIndices(numIndices); if (type == TAG_TILE_MESH) {
decodeWayIndices(numIndices, false);
} else {
coordCnt = decodeWayIndices(numIndices, true);
// otherwise using TAG_ELEM_NUM_COORDINATES
}
break; break;
case TAG_ELEM_COORDS: case TAG_ELEM_COORDS:
@ -296,14 +309,27 @@ public class TileDecoder extends PbfDecoder {
log.debug("{} no coordinates", mTile); log.debug("{} no coordinates", mTile);
} }
mElem.ensurePointSize(coordCnt, false); if (type == TAG_TILE_MESH) {
int cnt = decodeInterleavedPoints(mElem, mScaleFactor); mElem.ensurePointSize((coordCnt * 3 / 2), false);
int cnt = decodeInterleavedPoints3D(mElem.points, 1);
if (cnt != coordCnt) { if (cnt != (3 * coordCnt)) {
log.debug("{} wrong number of coordintes {}/{}", mTile, log.debug("{} wrong number of coordintes {}/{}", mTile,
Integer.valueOf(coordCnt), Integer.valueOf(coordCnt),
Integer.valueOf(cnt)); Integer.valueOf(cnt));
fail = true; fail = true;
}
mElem.pointPos = cnt;
} else {
mElem.ensurePointSize(coordCnt, false);
int cnt = decodeInterleavedPoints(mElem, mScaleFactor);
if (cnt != coordCnt) {
log.debug("{} wrong number of coordintes {}/{}", mTile,
Integer.valueOf(coordCnt),
Integer.valueOf(cnt));
fail = true;
}
} }
break; break;
@ -317,9 +343,8 @@ public class TileDecoder extends PbfDecoder {
} }
if (fail || numTags == 0 || numIndices == 0) { if (fail || numTags == 0 || numIndices == 0) {
log.debug("{} failed: bytes:{} index:{} tags:{} ({},{})", log.debug("{} failed: bytes:{} tags:{} ({},{})",
mTile, Integer.valueOf(bytes), mTile, Integer.valueOf(bytes),
Arrays.toString(index),
mElem.tags, mElem.tags,
Integer.valueOf(numIndices), Integer.valueOf(numIndices),
Integer.valueOf(coordCnt)); Integer.valueOf(coordCnt));
@ -336,6 +361,9 @@ public class TileDecoder extends PbfDecoder {
case TAG_TILE_POINT: case TAG_TILE_POINT:
mElem.type = GeometryType.POINT; mElem.type = GeometryType.POINT;
break; break;
case TAG_TILE_MESH:
mElem.type = GeometryType.TRIS;
break;
} }
mMapDataSink.process(mElem); mMapDataSink.process(mElem);

View File

@ -0,0 +1,641 @@
package org.oscim.utils;
/*
* Copyright 2014 Hannes Janetzek
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.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/>.
*/
/**
* Stripped down HashMap making HashItem entries public - So you have your custom
* 'Entry' holding key and value. HashItem must implement equals() and hashCode()
* only for the 'key' part.
*
* KeyMap.put(HashItem, boolean replace) allows to get or add an item in one invocation.
*
* TODO add to NOTICE file
* The VTM library includes software developed as part of the Apache
* Harmony project which is copyright 2006, The Apache Software Foundation and
* released under the Apache License 2.0. http://harmony.apache.org
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.util.Arrays;
import org.oscim.utils.KeyMap.HashItem;
import org.oscim.utils.pool.Inlist;
/**
* <p>
* Note: the implementation of {@code KeyMap} is not synchronized. If one thread
* of several threads accessing an instance modifies the map structurally,
* access to the map needs to be synchronized. A structural modification is an
* operation that adds or removes an entry. Changes in the value of an entry are
* not structural changes.
*
* @param <K> the type of keys maintained by this map
*/
public class KeyMap<K extends HashItem> extends Inlist<KeyMap<K>> {
/**
* Min capacity (other than zero) for a HashMap. Must be a power of two
* greater than 1 (and less than 1 << 30).
*/
private static final int MINIMUM_CAPACITY = 4;
/**
* Max capacity for a HashMap. Must be a power of two >= MINIMUM_CAPACITY.
*/
private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* An empty table shared by all zero-capacity maps (typically from default
* constructor). It is never written to, and replaced on first put. Its size
* is set to half the minimum, so that the first resize will create a
* minimum-sized table.
*/
private static final HashItem[] EMPTY_TABLE = new HashItem[MINIMUM_CAPACITY >>> 1];
/**
* The default load factor. Note that this implementation ignores the
* load factor, but cannot do away with it entirely because it's
* mentioned in the API.
*
* <p>
* Note that this constant has no impact on the behavior of the program, but
* it is emitted as part of the serialized form. The load factor of .75 is
* hardwired into the program, which uses cheap shifts in place of expensive
* division.
*/
static final float DEFAULT_LOAD_FACTOR = .75F;
/**
* The hash table. If this hash map contains a mapping for null, it is
* not represented this hash table.
*/
HashItem[] table;
/**
* The number of mappings in this hash map.
*/
int size;
/**
* The table is rehashed when its size exceeds this threshold.
* The value of this field is generally .75 * capacity, except when
* the capacity is zero, as described in the EMPTY_TABLE declaration
* above.
*/
private int threshold;
/**
* Constructs a new empty {@code HashMap} instance.
*/
public KeyMap() {
table = (HashItem[]) EMPTY_TABLE;
threshold = -1; // Forces first put invocation to replace EMPTY_TABLE
}
/**
* Constructs a new {@code HashMap} instance with the specified capacity.
*
* @param capacity
* the initial capacity of this hash map.
* @throws IllegalArgumentException
* when the capacity is less than zero.
*/
public KeyMap(int capacity) {
if (capacity < 0) {
throw new IllegalArgumentException("Capacity: " + capacity);
}
if (capacity == 0) {
HashItem[] tab = (HashItem[]) EMPTY_TABLE;
table = tab;
threshold = -1; // Forces first put() to replace EMPTY_TABLE
return;
}
if (capacity < MINIMUM_CAPACITY) {
capacity = MINIMUM_CAPACITY;
} else if (capacity > MAXIMUM_CAPACITY) {
capacity = MAXIMUM_CAPACITY;
} else {
capacity = roundUpToPowerOfTwo(capacity);
}
makeTable(capacity);
}
/**
* Constructs a new {@code HashMap} instance with the specified capacity and
* load factor.
*
* @param capacity
* the initial capacity of this hash map.
* @param loadFactor
* the initial load factor.
* @throws IllegalArgumentException
* when the capacity is less than zero or the load factor is
* less or equal to zero or NaN.
*/
public KeyMap(int capacity, float loadFactor) {
this(capacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new IllegalArgumentException("Load factor: " + loadFactor);
}
/* Note that this implementation ignores loadFactor; it always uses
* a load factor of 3/4. This simplifies the code and generally
* improves performance. */
}
/**
* Returns an appropriate capacity for the specified initial size. Does
* not round the result up to a power of two; the caller must do this!
* The returned value will be between 0 and MAXIMUM_CAPACITY (inclusive).
*/
static int capacityForInitSize(int size) {
int result = (size >> 1) + size; // Multiply by 3/2 to allow for growth
// boolean expr is equivalent to result >= 0 && result<MAXIMUM_CAPACITY
return (result & ~(MAXIMUM_CAPACITY - 1)) == 0 ? result : MAXIMUM_CAPACITY;
}
/**
* This method is called from the pseudo-constructors (clone and readObject)
* prior to invoking constructorPut/constructorPutAll, which invoke the
* overridden constructorNewEntry method. Normally it is a VERY bad idea to
* invoke an overridden method from a pseudo-constructor (Effective Java
* Item 17). In this case it is unavoidable, and the init method provides a
* workaround.
*/
void init() {
}
/**
* Returns whether this map is empty.
*
* @return {@code true} if this map has no elements, {@code false}
* otherwise.
* @see #size()
*/
public boolean isEmpty() {
return size == 0;
}
/**
* Returns the number of elements in this map.
*
* @return the number of elements in this map.
*/
public int size() {
return size;
}
/**
* Returns the value of the mapping with the specified key.
*
* @param key
* the key.
* @return the value of the mapping with the specified key, or {@code null}
* if no mapping for the specified key is found.
*/
@SuppressWarnings("unchecked")
public K get(HashItem key) {
// if (key == null) {
// HashItem e = entryForNullKey;
// return e == null ? null : e.key;
// }
// Doug Lea's supplemental secondaryHash function (inlined)
int hash = key.hashCode();
hash ^= (hash >>> 20) ^ (hash >>> 12);
hash ^= (hash >>> 7) ^ (hash >>> 4);
HashItem[] tab = table;
for (HashItem e = tab[hash & (tab.length - 1)]; e != null; e = e.next) {
HashItem eKey = e;
if (eKey == key || (e.hash == hash && key.equals(eKey))) {
return (K) e;
}
}
return null;
}
// /**
// * Returns whether this map contains the specified key.
// *
// * @param key
// * the key to search for.
// * @return {@code true} if this map contains the specified key,
// * {@code false} otherwise.
// */
// //@Override
// public boolean containsKey(Object key) {
// if (key == null) {
// return entryForNullKey != null;
// }
//
// // Doug Lea's supplemental secondaryHash function (inlined)
// int hash = key.hashCode();
// hash ^= (hash >>> 20) ^ (hash >>> 12);
// hash ^= (hash >>> 7) ^ (hash >>> 4);
//
// HashItem[] tab = table;
// for (HashItem e = tab[hash & (tab.length - 1)]; e != null; e = e.next) {
// K eKey = e.key;
// if (eKey == key || (e.hash == hash && key.equals(eKey))) {
// return true;
// }
// }
// return false;
// }
// /**
// * Returns whether this map contains the specified value.
// *
// * @param value
// * the value to search for.
// * @return {@code true} if this map contains the specified value,
// * {@code false} otherwise.
// */
// @Override
// public boolean containsValue(Object value) {
// HashMapEntry[] tab = table;
// int len = tab.length;
// if (value == null) {
// for (int i = 0; i < len; i++) {
// for (HashMapEntry e = tab[i]; e != null; e = e.next) {
// if (e.value == null) {
// return true;
// }
// }
// }
// return entryForNullKey != null && entryForNullKey.value == null;
// }
//
// // value is non-null
// for (int i = 0; i < len; i++) {
// for (HashMapEntry e = tab[i]; e != null; e = e.next) {
// if (value.equals(e.value)) {
// return true;
// }
// }
// }
// return entryForNullKey != null && value.equals(entryForNullKey.value);
// }
/**
* Maps the specified key to the specified value.
*
* @param key
* the key.
* @param value
* the value.
* @return the value of any previous mapping with the specified key or
* {@code null} if there was no such mapping.
*/
public K put(K key) {
return put(key, true);
}
@SuppressWarnings("unchecked")
public K put(K key, boolean replace) {
int hash = secondaryHash(key.hashCode());
HashItem[] tab = table;
int index = hash & (tab.length - 1);
for (HashItem e = tab[index]; e != null; e = e.next) {
if (e.hash == hash && key.equals(e)) {
if (replace) {
tab[index] = Inlist.remove(tab[index], e);
tab[index] = Inlist.push(tab[index], key);
}
//V oldValue = e.value;
//e.value = value;
return (K) e; //oldValue;
}
}
// No entry key is present; create one
if (size++ > threshold) {
tab = doubleCapacity();
index = hash & (tab.length - 1);
}
addNewEntry(key, hash, index);
return null;
}
// public K put(K key) {
// // if (key == null) {
// // return putValueForNullKey(value);
// // }
//
// int hash = secondaryHash(key.hashCode());
// HashItem[] tab = table;
// int index = hash & (tab.length - 1);
// for (HashItem e = tab[index]; e != null; e = e.next) {
// if (e.hash == hash && key.equals(e.key)) {
// preModify(e);
// //V oldValue = e.value;
// //e.value = value;
// return e.key; //oldValue;
// }
// }
//
// // No entry for (non-null) key is present; create one
// modCount++;
// if (size++ > threshold) {
// tab = doubleCapacity();
// index = hash & (tab.length - 1);
// }
// addNewEntry(key, hash, index);
// return null;
// }
// private V putValueForNullKey(V value) {
// HashMapEntry<K> entry = entryForNullKey;
// if (entry == null) {
// addNewEntryForNullKey(value);
// size++;
// modCount++;
// return null;
// } else {
// preModify(entry);
// V oldValue = entry.value;
// entry.value = value;
// return oldValue;
// }
// }
/**
* Creates a new entry for the given key, value, hash, and index and
* inserts it into the hash table. This method is called by put
* (and indirectly, putAll), and overridden by LinkedHashMap. The hash
* must incorporate the secondary hash function.
*/
void addNewEntry(K key, int hash, int index) {
key.setIndex(hash, table[index]);
table[index] = key;
}
///**
// * Ensures that the hash table has sufficient capacity to store the
// * specified number of mappings, with room to grow. If not, it increases the
// * capacity as appropriate. Like doubleCapacity, this method moves existing
// * entries to new buckets as appropriate. Unlike doubleCapacity, this method
// * can grow the table by factors of 2^n for n > 1. Hopefully, a single call
// * to this method will be faster than multiple calls to doubleCapacity.
// *
// * <p>
// * This method is called only by putAll.
// */
//private void ensureCapacity(int numMappings) {
// int newCapacity = roundUpToPowerOfTwo(capacityForInitSize(numMappings));
// HashItem[] oldTable = table;
// int oldCapacity = oldTable.length;
// if (newCapacity <= oldCapacity) {
// return;
// }
// if (newCapacity == oldCapacity * 2) {
// doubleCapacity();
// return;
// }
//
// // We're growing by at least 4x, rehash in the obvious way
// HashItem[] newTable = makeTable(newCapacity);
// if (size != 0) {
// int newMask = newCapacity - 1;
// for (int i = 0; i < oldCapacity; i++) {
// for (HashItem e = oldTable[i]; e != null;) {
// HashItem oldNext = e.next;
// int newIndex = e.hash & newMask;
// HashItem newNext = newTable[newIndex];
// newTable[newIndex] = e;
// e.next = newNext;
// e = oldNext;
// }
// }
// }
//}
/**
* Allocate a table of the given capacity and set the threshold accordingly.
*
* @param newCapacity must be a power of two
*/
private HashItem[] makeTable(int newCapacity) {
HashItem[] newTable = (HashItem[]) new HashItem[newCapacity];
table = newTable;
threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity
return newTable;
}
/**
* Doubles the capacity of the hash table. Existing entries are placed in
* the correct bucket on the enlarged table. If the current capacity is,
* MAXIMUM_CAPACITY, this method is a no-op. Returns the table, which
* will be new unless we were already at MAXIMUM_CAPACITY.
*/
private HashItem[] doubleCapacity() {
HashItem[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
return oldTable;
}
int newCapacity = oldCapacity * 2;
HashItem[] newTable = makeTable(newCapacity);
if (size == 0) {
return newTable;
}
for (int j = 0; j < oldCapacity; j++) {
/* Rehash the bucket using the minimum number of field writes.
* This is the most subtle and delicate code in the class. */
HashItem e = oldTable[j];
if (e == null) {
continue;
}
int highBit = e.hash & oldCapacity;
HashItem broken = null;
newTable[j | highBit] = e;
for (HashItem n = e.next; n != null; e = n, n = n.next) {
int nextHighBit = n.hash & oldCapacity;
if (nextHighBit != highBit) {
if (broken == null)
newTable[j | nextHighBit] = n;
else
broken.next = n;
broken = e;
highBit = nextHighBit;
}
}
if (broken != null)
broken.next = null;
}
return newTable;
}
// /**
// * Removes the mapping with the specified key from this map.
// *
// * @param key
// * the key of the mapping to remove.
// * @return the value of the removed mapping or {@code null} if no mapping
// * for the specified key was found.
// */
// @Override
// public V remove(Object key) {
// if (key == null) {
// return removeNullKey();
// }
// int hash = secondaryHash(key.hashCode());
// HashMapEntry<K>[] tab = table;
// int index = hash & (tab.length - 1);
// for (HashMapEntry<K> e = tab[index], prev = null; e != null; prev = e, e = e.next) {
// if (e.hash == hash && key.equals(e.key)) {
// if (prev == null) {
// tab[index] = e.next;
// } else {
// prev.next = e.next;
// }
// modCount++;
// size--;
// postRemove(e);
// return e.value;
// }
// }
// return null;
// }
/**
* Subclass overrides this method to unlink entry.
*/
void postRemove(HashItem e) {
}
/**
* Removes all mappings from this hash map, leaving it empty.
*
* @see #isEmpty
* @see #size
*/
public void clear() {
if (size != 0) {
Arrays.fill(table, null);
size = 0;
}
}
static final boolean STATS = false;
@SuppressWarnings("unchecked")
public K releaseItems() {
if (size == 0)
return null;
int collisions = 0;
int max = 0;
int sum = 0;
HashItem items = null;
HashItem last;
for (int i = 0, n = table.length; i < n; i++) {
HashItem item = table[i];
if (item == null)
continue;
table[i] = null;
if (STATS) {
sum = 0;
last = item;
while (last != null) {
if (last.next == null)
break;
sum++;
last = last.next;
}
max = Math.max(max, sum);
collisions += sum;
} else {
last = Inlist.last(item);
}
last.next = items;
items = item;
}
if (STATS)
System.out.println("collisions: " + collisions + " " + max + " " + size);
Arrays.fill(table, null);
size = 0;
return (K) items;
}
public static class HashItem extends Inlist<HashItem> {
int hash;
public void setIndex(int hash, HashItem next) {
this.hash = hash;
this.next = next;
}
}
/**
* Applies a supplemental hash function to a given hashCode, which defends
* against poor quality hash functions. This is critical because HashMap
* uses power-of-two length hash tables, that otherwise encounter collisions
* for hashCodes that do not differ in lower or upper bits.
*/
private static int secondaryHash(int h) {
// Doug Lea's supplemental hash function
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**
* Returns the smallest power of two >= its argument, with several caveats:
* If the argument is negative but not Integer.MIN_VALUE, the method returns
* zero. If the argument is > 2^30 or equal to Integer.MIN_VALUE, the method
* returns Integer.MIN_VALUE. If the argument is zero, the method returns
* zero.
*/
private static int roundUpToPowerOfTwo(int i) {
i--; // If input is a power of two, shift its high-order bit right
// "Smear" the high-order bit all the way to the right
i |= i >>> 1;
i |= i >>> 2;
i |= i >>> 4;
i |= i >>> 8;
i |= i >>> 16;
return i + 1;
}
}