Merge branch 's3db'
This commit is contained in:
commit
7673ab16e5
@ -34,10 +34,14 @@
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="org.oscim.android.test.PathOverlayActivity"
|
||||
android:name="org.oscim.android.test.S3DBMapActivity"
|
||||
android:label="@string/title_activity_map" >
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="org.oscim.android.test.PathOverlayActivity"
|
||||
android:label="@string/title_activity_map" >
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="org.oscim.android.test.MarkerOverlayActivity"
|
||||
|
@ -30,7 +30,7 @@ import android.view.MenuItem;
|
||||
|
||||
public class BaseMapActivity extends MapActivity {
|
||||
|
||||
private final static boolean USE_CACHE = true;
|
||||
final static boolean USE_CACHE = true;
|
||||
|
||||
MapView mMapView;
|
||||
VectorTileLayer mBaseLayer;
|
||||
@ -70,7 +70,7 @@ public class BaseMapActivity extends MapActivity {
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (USE_CACHE)
|
||||
if (mCache != null)
|
||||
mCache.dispose();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -46,6 +46,7 @@ public class Samples extends Activity {
|
||||
linearLayout.addView(createButton(PathOverlayActivity.class));
|
||||
linearLayout.addView(createButton(MarkerOverlayActivity.class));
|
||||
linearLayout.addView(createButton(ThemeStylerActivity.class));
|
||||
linearLayout.addView(createButton(S3DBMapActivity.class));
|
||||
}
|
||||
|
||||
private Button createButton(final Class<?> clazz) {
|
||||
|
@ -266,4 +266,8 @@ public class MapTile extends Tile {
|
||||
data = td;
|
||||
}
|
||||
}
|
||||
|
||||
public static int depthOffset(MapTile t) {
|
||||
return ((t.tileX % 4) + (t.tileY % 4 * 4) + 1);
|
||||
}
|
||||
}
|
||||
|
@ -196,6 +196,15 @@ public class TileManager {
|
||||
* jobs come in. */
|
||||
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);
|
||||
|
||||
if (mZoomTable != null) {
|
||||
@ -349,7 +358,7 @@ public class TileManager {
|
||||
mJobs.add(tile);
|
||||
}
|
||||
|
||||
if ((zoomLevel > 2) && (mZoomTable == null)) {
|
||||
if ((zoomLevel > mMinZoom) && (mZoomTable == null)) {
|
||||
/* prefetch parent */
|
||||
MapTile p = tile.node.parent.item;
|
||||
if (p == null) {
|
||||
|
@ -226,6 +226,10 @@ public abstract class TileRenderer extends LayerRenderer {
|
||||
for (int i = 0; i < mDrawTiles.cnt; i++)
|
||||
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 */
|
||||
mProxyTileCnt = 0;
|
||||
|
||||
@ -287,6 +291,7 @@ public abstract class TileRenderer extends LayerRenderer {
|
||||
private final ScanBox mScanBox = new ScanBox() {
|
||||
@Override
|
||||
protected void setVisible(int y, int x1, int x2) {
|
||||
|
||||
MapTile[] tiles = mDrawTiles.tiles;
|
||||
int cnt = mDrawTiles.cnt;
|
||||
|
||||
|
168
vtm/src/org/oscim/layers/tile/s3db/S3DBLayer.java
Normal file
168
vtm/src/org/oscim/layers/tile/s3db/S3DBLayer.java
Normal 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;
|
||||
}
|
||||
}
|
124
vtm/src/org/oscim/layers/tile/s3db/S3DBTileLoader.java
Normal file
124
vtm/src/org/oscim/layers/tile/s3db/S3DBTileLoader.java
Normal 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);
|
||||
}
|
||||
}
|
@ -34,19 +34,30 @@ import org.oscim.utils.FastMath;
|
||||
public class BuildingLayer extends Layer implements TileLoaderThemeHook {
|
||||
//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) {
|
||||
super(map);
|
||||
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;
|
||||
|
||||
@Override
|
||||
public void update(GLViewport v) {
|
||||
|
||||
boolean show = v.pos.scale >= (1 << MIN_ZOOM);
|
||||
boolean show = v.pos.scale >= (1 << mMinZoom);
|
||||
|
||||
if (show) {
|
||||
if (mAlpha < 1) {
|
||||
@ -79,17 +90,10 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
|
||||
super.update(v);
|
||||
}
|
||||
};
|
||||
|
||||
//mExtLayer.setColors(Color.LTGRAY, Color.GRAY, Color.DKGRAY);
|
||||
mRenderer = mExtLayer;
|
||||
}
|
||||
|
||||
//private int multi;
|
||||
|
||||
private final float mFadeTime = 500;
|
||||
|
||||
private final static int MIN_ZOOM = 17;
|
||||
|
||||
@Override
|
||||
public boolean render(MapTile tile, ElementLayers layers, MapElement element,
|
||||
RenderStyle style, int level) {
|
||||
@ -129,6 +133,7 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
|
||||
return true;
|
||||
}
|
||||
|
||||
//private int multi;
|
||||
//@Override
|
||||
//public boolean onTouchEvent(MotionEvent e) {
|
||||
// int action = e.getAction() & MotionEvent.ACTION_MASK;
|
||||
|
@ -140,9 +140,7 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
|
||||
}
|
||||
|
||||
public void setDataSource(ITileDataSource dataSource) {
|
||||
if (mTileDataSource != null)
|
||||
mTileDataSource.destroy();
|
||||
|
||||
cleanup();
|
||||
mTileDataSource = dataSource;
|
||||
}
|
||||
|
||||
|
@ -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.READY;
|
||||
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import org.oscim.backend.GL20;
|
||||
import org.oscim.backend.canvas.Color;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.layers.tile.MapTile;
|
||||
import org.oscim.layers.tile.TileRenderer;
|
||||
@ -37,21 +38,26 @@ public class ExtrusionRenderer extends LayerRenderer {
|
||||
static final Logger log = LoggerFactory.getLogger(ExtrusionRenderer.class);
|
||||
|
||||
private final TileRenderer mTileLayer;
|
||||
private final int mTileZoom;
|
||||
private final boolean drawAlpha;
|
||||
|
||||
protected float mAlpha = 1;
|
||||
|
||||
public ExtrusionRenderer(TileRenderer tileRenderLayer) {
|
||||
public ExtrusionRenderer(TileRenderer tileRenderLayer, int tileZoom) {
|
||||
mTileLayer = tileRenderLayer;
|
||||
mTileSet = new TileSet();
|
||||
mTileZoom = tileZoom;
|
||||
mMode = 0;
|
||||
drawAlpha = true;
|
||||
}
|
||||
|
||||
private static int[] shaderProgram = new int[2];
|
||||
private static int[] hVertexPosition = new int[2];
|
||||
private static int[] hLightPosition = new int[2];
|
||||
private static int[] hMatrix = new int[2];
|
||||
private static int[] hColor = new int[2];
|
||||
private static int[] hAlpha = new int[2];
|
||||
private static int[] hMode = new int[2];
|
||||
public ExtrusionRenderer(TileRenderer tileRenderLayer, int tileZoom, boolean mesh, boolean alpha) {
|
||||
mTileLayer = tileRenderLayer;
|
||||
mTileSet = new TileSet();
|
||||
mTileZoom = tileZoom;
|
||||
mMode = mesh ? 1 : 0;
|
||||
drawAlpha = false; //alpha;
|
||||
}
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
@ -59,57 +65,46 @@ public class ExtrusionRenderer extends LayerRenderer {
|
||||
private MapTile[] mTiles;
|
||||
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() {
|
||||
initialized = true;
|
||||
|
||||
for (int i = 0; i <= SHADER; i++) {
|
||||
if (i == 0) {
|
||||
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");
|
||||
}
|
||||
mShader[0] = new Shader("extrusion_layer_ext");
|
||||
mShader[1] = new Shader("extrusion_layer_mesh");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void update(GLViewport v) {
|
||||
public void update(GLViewport v) {
|
||||
|
||||
if (!initialized && !initShader())
|
||||
return;
|
||||
|
||||
if (shaderProgram[0] == 0)
|
||||
return;
|
||||
|
||||
if (mAlpha == 0 || v.pos.zoomLevel < 16) {
|
||||
if (mAlpha == 0 || v.pos.zoomLevel < mTileZoom) {
|
||||
setReady(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mUpdateColors) {
|
||||
synchronized (this) {
|
||||
System.arraycopy(mNewColors, 0, mColor, 0, 16);
|
||||
mUpdateColors = false;
|
||||
}
|
||||
}
|
||||
|
||||
int activeTiles = 0;
|
||||
mTileLayer.getVisibleTiles(mTileSet);
|
||||
MapTile[] tiles = mTileSet.tiles;
|
||||
@ -120,30 +115,38 @@ public class ExtrusionRenderer extends LayerRenderer {
|
||||
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)
|
||||
mTiles = new MapTile[mTileSet.cnt * 4];
|
||||
|
||||
int zoom = tiles[0].zoomLevel;
|
||||
|
||||
ExtrusionLayer el;
|
||||
if (zoom == 17) {
|
||||
if (zoom == mTileZoom) {
|
||||
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)
|
||||
continue;
|
||||
|
||||
if (!el.compiled) {
|
||||
int numShorts = el.mNumVertices * 8;
|
||||
el.compile(MapRenderer.getShortBuffer(numShorts));
|
||||
GLUtils.checkGlError("...");
|
||||
if (compileLayers(el))
|
||||
mTiles[activeTiles++] = t;
|
||||
}
|
||||
|
||||
if (el.compiled)
|
||||
mTiles[activeTiles++] = tiles[i];
|
||||
}
|
||||
} else if (zoom == 16) {
|
||||
// check if proxy children are ready
|
||||
} else if (zoom == mTileZoom - 1) {
|
||||
/* check if proxy children are ready */
|
||||
for (int i = 0; i < mTileSet.cnt; i++) {
|
||||
MapTile t = tiles[i];
|
||||
for (byte j = 0; j < 4; j++) {
|
||||
@ -162,6 +165,7 @@ public class ExtrusionRenderer extends LayerRenderer {
|
||||
}
|
||||
|
||||
mTileCnt = activeTiles;
|
||||
//log.debug("" + activeTiles + " " + zoom);
|
||||
|
||||
if (activeTiles > 0)
|
||||
setReady(true);
|
||||
@ -169,6 +173,53 @@ public class ExtrusionRenderer extends LayerRenderer {
|
||||
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) {
|
||||
ElementLayers layers = t.getLayers();
|
||||
if (layers == null || !t.state(READY | NEW_DATA))
|
||||
@ -179,51 +230,58 @@ public class ExtrusionRenderer extends LayerRenderer {
|
||||
|
||||
private final boolean debug = false;
|
||||
|
||||
@Override
|
||||
protected void render(GLViewport v) {
|
||||
// TODO one could render in one pass to texture and then draw the texture
|
||||
// with alpha... might be faster and would allow postprocessing outlines.
|
||||
private void renderCombined(int vertexPointer, ExtrusionLayer el) {
|
||||
for (; el != null; el = el.next()) {
|
||||
|
||||
MapTile[] tiles = mTiles;
|
||||
|
||||
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) {
|
||||
GLState.useProgram(shaderProgram[SHADER]);
|
||||
|
||||
GLState.enableVertexArrays(uExtVertexPosition, uExtLightPosition);
|
||||
GL.glUniform1i(uExtMode, 0);
|
||||
GLUtils.glUniform4fv(uExtColor, 4, mColor);
|
||||
|
||||
GLState.test(false, false);
|
||||
|
||||
for (int i = 0; i < mTileCnt; i++) {
|
||||
ExtrusionLayer el = tiles[i].getLayers().getExtrusionLayers();
|
||||
|
||||
setMatrix(v, tiles[i], 0);
|
||||
v.mvp.setAsUniform(uExtMatrix);
|
||||
if (el.vboIndices == null)
|
||||
continue;
|
||||
|
||||
el.vboIndices.bind();
|
||||
el.vboVertices.bind();
|
||||
|
||||
GL.glVertexAttribPointer(uExtVertexPosition, 3,
|
||||
GL.glVertexAttribPointer(vertexPointer, 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]),
|
||||
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);
|
||||
|
||||
GL.glDrawElements(GL20.GL_LINES, el.mIndiceCnt[3],
|
||||
GL20.GL_UNSIGNED_SHORT,
|
||||
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]) * 2);
|
||||
if (el.numIndices[2] > 0) {
|
||||
int offset = sumIndices * 2;
|
||||
GL.glDrawElements(GL20.GL_TRIANGLES, el.numIndices[4],
|
||||
GL20.GL_UNSIGNED_SHORT, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GLViewport v) {
|
||||
// TODO one could render in one pass to texture and then draw the texture
|
||||
// with alpha... might be faster and would allow postprocessing outlines.
|
||||
|
||||
MapTile[] tiles = mTiles;
|
||||
Shader s = mShader[mMode];
|
||||
|
||||
if (debug) {
|
||||
s.useProgram();
|
||||
|
||||
//GLState.useProgram(shaderProgram[mMode]);
|
||||
|
||||
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.blend(true);
|
||||
for (int i = 0; i < mTileCnt; i++) {
|
||||
ExtrusionLayer el = tiles[i].getLayers().getExtrusionLayers();
|
||||
|
||||
setMatrix(v, tiles[i], 0);
|
||||
v.mvp.setAsUniform(s.uMVP);
|
||||
|
||||
renderCombined(s.aPos, el);
|
||||
|
||||
// just a temporary reference!
|
||||
tiles[i] = null;
|
||||
@ -232,49 +290,48 @@ public class ExtrusionRenderer extends LayerRenderer {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
GLState.test(true, false);
|
||||
|
||||
GLState.useProgram(shaderProgram[SHADER]);
|
||||
GLState.enableVertexArrays(uExtVertexPosition, -1);
|
||||
if (v.pos.scale < (1 << 18)) {
|
||||
// chances are high that one moves through a building
|
||||
// with scale > 2 also draw back sides in this case.
|
||||
GL.glEnable(GL20.GL_CULL_FACE);
|
||||
}
|
||||
GL.glDepthFunc(GL20.GL_LESS);
|
||||
GL.glColorMask(false, false, false, false);
|
||||
GL.glUniform1i(uExtMode, -1);
|
||||
//GLUtils.glUniform4fv(uExtColor, 4, mColor);
|
||||
GL.glUniform1f(uExtAlpha, mAlpha);
|
||||
s.useProgram();
|
||||
//GLState.useProgram(shaderProgram[mMode]);
|
||||
GLState.enableVertexArrays(s.aPos, -1);
|
||||
GLState.blend(false);
|
||||
|
||||
// draw to depth buffer
|
||||
GL.glEnable(GL20.GL_CULL_FACE);
|
||||
GL.glDepthFunc(GL20.GL_LESS);
|
||||
|
||||
//GL.glUniform1f(uExtAlpha, mAlpha);
|
||||
GL.glUniform1f(s.uAlpha, 1);
|
||||
|
||||
if (drawAlpha) {
|
||||
GL.glColorMask(false, false, false, false);
|
||||
GL.glUniform1i(s.uMode, -1);
|
||||
//GLUtils.glUniform4fv(uExtColor, 4, mColor);
|
||||
|
||||
/* draw to depth buffer */
|
||||
for (int i = 0; i < mTileCnt; i++) {
|
||||
MapTile t = tiles[i];
|
||||
ExtrusionLayer el = t.getLayers().getExtrusionLayers();
|
||||
int d = MapRenderer.depthOffset(t) * 10;
|
||||
if (el == null)
|
||||
continue;
|
||||
|
||||
int d = MapTile.depthOffset(t) * 10;
|
||||
|
||||
setMatrix(v, t, d);
|
||||
v.mvp.setAsUniform(uExtMatrix);
|
||||
v.mvp.setAsUniform(s.uMVP);
|
||||
|
||||
el.vboIndices.bind();
|
||||
el.vboVertices.bind();
|
||||
|
||||
GL.glVertexAttribPointer(uExtVertexPosition, 3,
|
||||
GL20.GL_SHORT, false, 8, 0);
|
||||
|
||||
GL.glDrawElements(GL20.GL_TRIANGLES,
|
||||
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]),
|
||||
GL20.GL_UNSIGNED_SHORT, 0);
|
||||
renderCombined(s.aPos, el);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
@ -282,60 +339,100 @@ public class ExtrusionRenderer extends LayerRenderer {
|
||||
MapTile t = tiles[i];
|
||||
ExtrusionLayer el = t.getLayers().getExtrusionLayers();
|
||||
|
||||
if (el.colors == null) {
|
||||
currentColor = mColor;
|
||||
GLUtils.glUniform4fv(uExtColor, 4, currentColor);
|
||||
} else if (currentColor != el.colors) {
|
||||
currentColor = el.colors;
|
||||
GLUtils.glUniform4fv(uExtColor, 4, currentColor);
|
||||
if (el == null)
|
||||
continue;
|
||||
|
||||
if (el.vboIndices == null)
|
||||
continue;
|
||||
|
||||
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);
|
||||
v.mvp.setAsUniform(uExtMatrix);
|
||||
v.mvp.setAsUniform(s.uMVP);
|
||||
|
||||
el.vboIndices.bind();
|
||||
el.vboVertices.bind();
|
||||
|
||||
GL.glVertexAttribPointer(uExtVertexPosition, 3,
|
||||
GL20.GL_SHORT, false, 8, 0);
|
||||
for (; el != null; el = el.next()) {
|
||||
|
||||
GL.glVertexAttribPointer(uExtLightPosition, 2,
|
||||
GL20.GL_UNSIGNED_BYTE, false, 8, 6);
|
||||
if (el.colors != currentColor) {
|
||||
currentColor = el.colors;
|
||||
GLUtils.glUniform4fv(s.uColor, mMode == 0 ? 4 : 1,
|
||||
el.colors);
|
||||
}
|
||||
|
||||
// draw roof
|
||||
GL.glUniform1i(uExtMode, 0);
|
||||
GL.glDrawElements(GL20.GL_TRIANGLES, el.mIndiceCnt[2],
|
||||
GL20.GL_UNSIGNED_SHORT, (el.mIndiceCnt[0] + el.mIndiceCnt[1]) * 2);
|
||||
/* indices offset */
|
||||
int indexOffset = el.indexOffset;
|
||||
/* vertex byte offset */
|
||||
int vertexOffset = el.getOffset();
|
||||
|
||||
// draw sides 1
|
||||
GL.glUniform1i(uExtMode, 1);
|
||||
GL.glDrawElements(GL20.GL_TRIANGLES, el.mIndiceCnt[0],
|
||||
GL.glVertexAttribPointer(s.aPos, 3,
|
||||
GL20.GL_SHORT, false, 8, vertexOffset);
|
||||
|
||||
GL.glVertexAttribPointer(s.aLight, 2,
|
||||
GL20.GL_UNSIGNED_BYTE, false, 8, vertexOffset + 6);
|
||||
|
||||
/* draw extruded outlines */
|
||||
if (el.numIndices[0] > 0) {
|
||||
/* 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);
|
||||
|
||||
/* draw sides 1 */
|
||||
GL.glUniform1i(s.uMode, 1);
|
||||
GL.glDrawElements(GL20.GL_TRIANGLES, el.numIndices[0],
|
||||
GL20.GL_UNSIGNED_SHORT, 0);
|
||||
|
||||
// draw sides 2
|
||||
GL.glUniform1i(uExtMode, 2);
|
||||
GL.glDrawElements(GL20.GL_TRIANGLES, el.mIndiceCnt[1],
|
||||
GL20.GL_UNSIGNED_SHORT, el.mIndiceCnt[0] * 2);
|
||||
/* draw sides 2 */
|
||||
GL.glUniform1i(s.uMode, 2);
|
||||
GL.glDrawElements(GL20.GL_TRIANGLES, el.numIndices[1],
|
||||
GL20.GL_UNSIGNED_SHORT, el.numIndices[0] * 2);
|
||||
|
||||
// drawing gl_lines with the same coordinates does not result in
|
||||
// same depth values as polygons, so add offset and draw gl_lequal:
|
||||
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(uExtMatrix);
|
||||
v.mvp.setAsUniform(s.uMVP);
|
||||
|
||||
GL.glUniform1i(uExtMode, 3);
|
||||
GL.glDrawElements(GL20.GL_LINES, el.mIndiceCnt[3],
|
||||
GL20.GL_UNSIGNED_SHORT,
|
||||
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]) * 2);
|
||||
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!
|
||||
tiles[i] = null;
|
||||
}
|
||||
|
||||
if (v.pos.scale < (1 << 18))
|
||||
GL.glDepthMask(false);
|
||||
GL.glDisable(GL20.GL_CULL_FACE);
|
||||
|
||||
GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
@ -343,150 +440,51 @@ public class ExtrusionRenderer extends LayerRenderer {
|
||||
mTileLayer.releaseTiles(mTileSet);
|
||||
}
|
||||
|
||||
private static void setMatrix(GLViewport m,
|
||||
MapTile tile, int delta) {
|
||||
|
||||
private static void setMatrix(GLViewport v, MapTile tile, int delta) {
|
||||
int z = tile.zoomLevel;
|
||||
double curScale = Tile.SIZE * m.pos.scale;
|
||||
float scale = (float) (m.pos.scale / (1 << z));
|
||||
double curScale = Tile.SIZE * v.pos.scale;
|
||||
float scale = (float) (v.pos.scale / (1 << z));
|
||||
|
||||
float x = (float) ((tile.x - m.pos.x) * curScale);
|
||||
float y = (float) ((tile.y - m.pos.y) * curScale);
|
||||
m.mvp.setTransScale(x, y, scale / MapRenderer.COORD_SCALE);
|
||||
float x = (float) ((tile.x - v.pos.x) * curScale);
|
||||
float y = (float) ((tile.y - v.pos.y) * curScale);
|
||||
v.mvp.setTransScale(x, y, scale / MapRenderer.COORD_SCALE);
|
||||
|
||||
// scale height
|
||||
m.mvp.setValue(10, scale / 10);
|
||||
// scale height ???
|
||||
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) {
|
||||
System.arraycopy(colors, 0, mNewColors, 0, 16);
|
||||
mUpdateColors = true;
|
||||
}
|
||||
private static float A = 0.88f;
|
||||
private static float R = 0xe9;
|
||||
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) {
|
||||
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 = {
|
||||
private static float[] DEBUG_COLOR = {
|
||||
// roof color
|
||||
_a * ((_r + _l) / 255),
|
||||
_a * ((_g + _l) / 255),
|
||||
_a * ((_b + _l) / 255),
|
||||
A * ((R + L) / 255),
|
||||
A * ((G + L) / 255),
|
||||
A * ((B + L) / 255),
|
||||
0.8f,
|
||||
// sligthly differ adjacent side
|
||||
// faces to improve contrast
|
||||
_a * ((_r - _s) / 255 + 0.01f),
|
||||
_a * ((_g - _s) / 255 + 0.01f),
|
||||
_a * ((_b - _s) / 255),
|
||||
_a,
|
||||
_a * ((_r - _s) / 255),
|
||||
_a * ((_g - _s) / 255),
|
||||
_a * ((_b - _s) / 255),
|
||||
_a,
|
||||
A * ((R - S) / 255 + 0.01f),
|
||||
A * ((G - S) / 255 + 0.01f),
|
||||
A * ((B - S) / 255),
|
||||
A,
|
||||
A * ((R - S) / 255),
|
||||
A * ((G - S) / 255),
|
||||
A * ((B - S) / 255),
|
||||
A,
|
||||
// roof outline
|
||||
(_r - _o) / 255,
|
||||
(_g - _o) / 255,
|
||||
(_b - _o) / 255,
|
||||
(R - O) / 255,
|
||||
(G - O) / 255,
|
||||
(B - O) / 255,
|
||||
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);"
|
||||
+ "}";
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ public abstract class LayerRenderer {
|
||||
isReady = ready;
|
||||
}
|
||||
|
||||
protected boolean isReady() {
|
||||
public boolean isReady() {
|
||||
return isReady;
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@ import java.nio.ShortBuffer;
|
||||
import org.oscim.backend.GL20;
|
||||
import org.oscim.backend.GLAdapter;
|
||||
import org.oscim.backend.canvas.Color;
|
||||
import org.oscim.layers.tile.MapTile;
|
||||
import org.oscim.map.Map;
|
||||
import org.oscim.renderer.elements.ElementLayers;
|
||||
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) {
|
||||
log.debug("onSurfaceChanged: new={}, {}x{}",
|
||||
mNewSurface, width, height);
|
||||
|
@ -269,16 +269,18 @@ public class ElementLayers extends TileData {
|
||||
return layer;
|
||||
}
|
||||
|
||||
private final static int[] VERTEX_SHORT_CNT = {
|
||||
4, // LINE_VERTEX_SHORTS
|
||||
6, // TEXLINE_VERTEX_SHORTS
|
||||
2, // POLY_VERTEX_SHORTS
|
||||
2, // MESH_VERTEX_SHORTS
|
||||
public final static int[] VERTEX_SHORT_CNT = {
|
||||
4, // LINE_VERTEX
|
||||
6, // TEXLINE_VERTEX
|
||||
2, // POLY_VERTEX
|
||||
2, // MESH_VERTEX
|
||||
4, // EXTRUSION_VERTEX
|
||||
};
|
||||
|
||||
private final static int TEXTURE_VERTEX_SHORTS = 6;
|
||||
private final static int SHORT_BYTES = 2;
|
||||
|
||||
// TODO move to specific layer implementation
|
||||
public int getSize() {
|
||||
int size = 0;
|
||||
|
||||
|
@ -18,24 +18,23 @@ package org.oscim.renderer.elements;
|
||||
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import org.oscim.backend.GL20;
|
||||
import org.oscim.backend.GLAdapter;
|
||||
import org.oscim.backend.canvas.Color;
|
||||
import org.oscim.core.GeometryBuffer;
|
||||
import org.oscim.core.GeometryBuffer.GeometryType;
|
||||
import org.oscim.core.MapElement;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.renderer.BufferObject;
|
||||
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.geom.LineClipper;
|
||||
import org.oscim.utils.pool.Inlist;
|
||||
import org.oscim.utils.pool.SyncPool;
|
||||
import org.slf4j.Logger;
|
||||
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 {
|
||||
static final Logger log = LoggerFactory.getLogger(ExtrusionLayer.class);
|
||||
|
||||
@ -46,13 +45,14 @@ public class ExtrusionLayer extends RenderElement {
|
||||
private final VertexItem mCurIndices[];
|
||||
private LineClipper mClipper;
|
||||
|
||||
/** 16 floats rgba for top, even-side, odd-sides and outline */
|
||||
public final float[] colors;
|
||||
public final int color;
|
||||
|
||||
// indices for:
|
||||
// 0. even sides, 1. odd sides, 2. roof, 3. roof outline
|
||||
public int mIndiceCnt[] = { 0, 0, 0, 0 };
|
||||
public int mNumIndices = 0;
|
||||
public int mNumVertices = 0;
|
||||
/** indices for: 0. even sides, 1. odd sides, 2. roof, 3. roof outline */
|
||||
public int numIndices[] = { 0, 0, 0, 0, 0 };
|
||||
public int sumVertices = 0;
|
||||
public int sumIndices = 0;
|
||||
|
||||
public BufferObject vboIndices;
|
||||
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_ODD_SIDE = 1;
|
||||
private final static int IND_ROOF = 2;
|
||||
|
||||
// FIXME flip OUTLINE / MESH!
|
||||
private final static int IND_OUTLINE = 3;
|
||||
private final static int IND_MESH = 4;
|
||||
|
||||
public boolean compiled = false;
|
||||
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) {
|
||||
super(RenderElement.EXTRUSION);
|
||||
this.level = level;
|
||||
this.colors = colors;
|
||||
this.color = 0;
|
||||
|
||||
mGroundResolution = groundResolution;
|
||||
mVertices = mCurVertices = VertexItem.pool.get();
|
||||
|
||||
mIndices = new VertexItem[4];
|
||||
mCurIndices = new VertexItem[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
mIndices = new VertexItem[5];
|
||||
mCurIndices = new VertexItem[5];
|
||||
for (int i = 0; i <= IND_MESH; i++)
|
||||
mIndices[i] = mCurIndices[i] = VertexItem.pool.get();
|
||||
|
||||
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) {
|
||||
|
||||
short[] index = element.index;
|
||||
float[] points = element.points;
|
||||
|
||||
// 10 cm steps
|
||||
/* 10 cm steps */
|
||||
float sfactor = 1 / 10f;
|
||||
height *= sfactor;
|
||||
minHeight *= sfactor;
|
||||
|
||||
// match height with ground resultion
|
||||
// (meter per pixel)
|
||||
/* match height with ground resultion (meter per pixel) */
|
||||
height /= mGroundResolution;
|
||||
minHeight /= mGroundResolution;
|
||||
|
||||
boolean complexOutline = false;
|
||||
boolean simpleOutline = true;
|
||||
|
||||
// current vertex id
|
||||
int startVertex = mNumVertices;
|
||||
/* current vertex id */
|
||||
int startVertex = sumVertices;
|
||||
int length = 0, ipos = 0, ppos = 0;
|
||||
|
||||
for (int n = index.length; ipos < n; ipos++, ppos += length) {
|
||||
length = index[ipos];
|
||||
|
||||
// end marker
|
||||
/* end marker */
|
||||
if (length < 0)
|
||||
break;
|
||||
|
||||
// start next polygon
|
||||
/* start next polygon */
|
||||
if (length == 0) {
|
||||
startVertex = mNumVertices;
|
||||
startVertex = sumVertices;
|
||||
simpleOutline = true;
|
||||
complexOutline = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check: drop last point from explicitly closed rings
|
||||
/* check: drop last point from explicitly closed rings */
|
||||
int len = length;
|
||||
if (points[ppos] == points[ppos + len - 2]
|
||||
&& points[ppos + 1] == points[ppos + len - 1]) {
|
||||
@ -126,11 +412,11 @@ public class ExtrusionLayer extends RenderElement {
|
||||
log.debug("explicit closed poly " + len);
|
||||
}
|
||||
|
||||
// need at least three points
|
||||
/* need at least three points */
|
||||
if (len < 6)
|
||||
continue;
|
||||
|
||||
// check if polygon contains inner rings
|
||||
/* check if polygon contains inner rings */
|
||||
if (simpleOutline && (ipos < n - 1) && (index[ipos + 1] > 0))
|
||||
simpleOutline = false;
|
||||
|
||||
@ -147,7 +433,7 @@ public class ExtrusionLayer extends RenderElement {
|
||||
}
|
||||
|
||||
private void addRoofSimple(int startVertex, int len) {
|
||||
// roof indices for convex shapes
|
||||
/* roof indices for convex shapes */
|
||||
int i = mCurIndices[IND_ROOF].used;
|
||||
short[] indices = mCurIndices[IND_ROOF].vertices;
|
||||
short first = (short) (startVertex + 1);
|
||||
@ -156,13 +442,15 @@ public class ExtrusionLayer extends RenderElement {
|
||||
if (i == VertexItem.SIZE) {
|
||||
mCurIndices[IND_ROOF].used = VertexItem.SIZE;
|
||||
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;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
indices[i++] = first;
|
||||
indices[i++] = (short) (first + k + 2);
|
||||
indices[i++] = (short) (first + k + 4);
|
||||
sumIndices += 3;
|
||||
}
|
||||
mCurIndices[IND_ROOF].used = i;
|
||||
}
|
||||
@ -174,13 +462,13 @@ public class ExtrusionLayer extends RenderElement {
|
||||
int len = 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++) {
|
||||
len += index[i];
|
||||
rings++;
|
||||
}
|
||||
|
||||
Tessellator.tessellate(points, ppos, len, index, ipos, rings,
|
||||
sumIndices += Tessellator.tessellate(points, ppos, len, index, ipos, rings,
|
||||
startVertex + 1, 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,
|
||||
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);
|
||||
int vertexCnt = len + (addFace ? 2 : 0);
|
||||
|
||||
@ -201,10 +489,10 @@ public class ExtrusionLayer extends RenderElement {
|
||||
float nx = points[pos + 0];
|
||||
float ny = points[pos + 1];
|
||||
|
||||
// vector to next point
|
||||
/* vector to next point */
|
||||
float vx = nx - cx;
|
||||
float vy = ny - cy;
|
||||
// vector from previous point
|
||||
/* vector from previous point */
|
||||
float ux, uy;
|
||||
|
||||
float a = (float) Math.sqrt(vx * vx + vy * vy);
|
||||
@ -217,8 +505,8 @@ public class ExtrusionLayer extends RenderElement {
|
||||
int changeY = 0;
|
||||
int angleSign = 0;
|
||||
|
||||
// vertex offset for all vertices in layer
|
||||
int vOffset = mNumVertices;
|
||||
/* vertex offset for all vertices in layer */
|
||||
int vOffset = sumVertices;
|
||||
|
||||
short[] vertices = mCurVertices.vertices;
|
||||
int v = mCurVertices.used;
|
||||
@ -241,15 +529,15 @@ public class ExtrusionLayer extends RenderElement {
|
||||
v = 0;
|
||||
}
|
||||
|
||||
// set coordinate
|
||||
/* set coordinate */
|
||||
vertices[v + 0] = vertices[v + 4] = (short) (cx * S);
|
||||
vertices[v + 1] = vertices[v + 5] = (short) (cy * S);
|
||||
|
||||
// set height
|
||||
/* set height */
|
||||
vertices[v + 2] = mh;
|
||||
vertices[v + 6] = h;
|
||||
|
||||
// get direction to next point
|
||||
/* get direction to next point */
|
||||
if (i < len) {
|
||||
nx = points[pos + i + 0];
|
||||
ny = points[pos + i + 1];
|
||||
@ -266,7 +554,7 @@ public class ExtrusionLayer extends RenderElement {
|
||||
vx = nx - cx;
|
||||
vy = ny - cy;
|
||||
|
||||
// set lighting (by direction)
|
||||
/* set lighting (by direction) */
|
||||
a = (float) Math.sqrt(vx * vx + vy * vy);
|
||||
color2 = (short) ((1 + vx / a) * 127);
|
||||
|
||||
@ -281,8 +569,8 @@ public class ExtrusionLayer extends RenderElement {
|
||||
|
||||
/* check if polygon is convex */
|
||||
if (convex) {
|
||||
// TODO simple polys with only one concave arc
|
||||
// could be handled without special triangulation
|
||||
/* TODO simple polys with only one concave arc
|
||||
* could be handled without special triangulation */
|
||||
if ((ux < 0 ? 1 : -1) != (vx < 0 ? 1 : -1))
|
||||
changeX++;
|
||||
if ((uy < 0 ? 1 : -1) != (vy < 0 ? 1 : -1))
|
||||
@ -306,7 +594,7 @@ public class ExtrusionLayer extends RenderElement {
|
||||
|
||||
/* check if face is within tile */
|
||||
if (mClipper.clipNext((int) nx, (int) ny) == 0) {
|
||||
even = (even == 0 ? 1 : 0);
|
||||
even = ++even % 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -317,99 +605,112 @@ public class ExtrusionLayer extends RenderElement {
|
||||
short s2 = 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) {
|
||||
s2 -= len;
|
||||
s3 -= len;
|
||||
}
|
||||
|
||||
short[] indices = mCurIndices[even].vertices;
|
||||
// index id relative to mCurIndices item
|
||||
int ind = mCurIndices[even].used;
|
||||
|
||||
if (ind == VertexItem.SIZE) {
|
||||
mCurIndices[even].next = VertexItem.pool.get();
|
||||
mCurIndices[even] = mCurIndices[even].next;
|
||||
indices = mCurIndices[even].vertices;
|
||||
ind = 0;
|
||||
VertexItem it = mCurIndices[even];
|
||||
if (it.used == VertexItem.SIZE) {
|
||||
it = VertexItem.pool.getNext(it);
|
||||
mCurIndices[even] = it;
|
||||
}
|
||||
|
||||
int ind = it.used;
|
||||
short[] indices = it.vertices;
|
||||
indices[ind + 0] = s0;
|
||||
indices[ind + 1] = s2;
|
||||
indices[ind + 2] = s1;
|
||||
|
||||
indices[ind + 3] = s1;
|
||||
indices[ind + 4] = s2;
|
||||
indices[ind + 5] = s3;
|
||||
it.used += 6;
|
||||
sumIndices += 6;
|
||||
|
||||
mCurIndices[even].used += 6;
|
||||
even = (even == 0 ? 1 : 0);
|
||||
/* flipp even-odd */
|
||||
even = ++even % 2;
|
||||
|
||||
/* add roof outline indices */
|
||||
VertexItem it = mCurIndices[IND_OUTLINE];
|
||||
it = mCurIndices[IND_OUTLINE];
|
||||
if (it.used == VertexItem.SIZE) {
|
||||
it.next = VertexItem.pool.get();
|
||||
it = mCurIndices[IND_OUTLINE] = it.next;
|
||||
it = VertexItem.pool.getNext(it);
|
||||
mCurIndices[IND_OUTLINE] = it;
|
||||
}
|
||||
it.vertices[it.used++] = s1;
|
||||
it.vertices[it.used++] = s3;
|
||||
ind = it.used;
|
||||
indices = it.vertices;
|
||||
indices[ind + 0] = s1;
|
||||
indices[ind + 1] = s3;
|
||||
it.used += 2;
|
||||
sumIndices += 2;
|
||||
}
|
||||
|
||||
mCurVertices.used = v;
|
||||
mNumVertices += vertexCnt;
|
||||
sumVertices += vertexCnt;
|
||||
return convex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(ShortBuffer sbuf) {
|
||||
public void compile(ShortBuffer vertexBuffer, ShortBuffer indexBuffer) {
|
||||
mClipper = null;
|
||||
|
||||
if (mNumVertices == 0 || compiled)
|
||||
if (compiled) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
if (mVertexMap != null) {
|
||||
vertexPool.releaseAll(mVertexMap.releaseItems());
|
||||
mVertexMap = vertexMapPool.release(mVertexMap);
|
||||
mVertexMap = null;
|
||||
}
|
||||
|
||||
if (sumVertices == 0) {
|
||||
compiled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mNumIndices = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
indexOffset = indexBuffer.position();
|
||||
|
||||
for (int i = 0; i <= IND_MESH; i++) {
|
||||
for (VertexItem vi = mIndices[i]; vi != null; vi = vi.next) {
|
||||
sbuf.put(vi.vertices, 0, vi.used);
|
||||
mIndiceCnt[i] += vi.used;
|
||||
indexBuffer.put(vi.vertices, 0, vi.used);
|
||||
numIndices[i] += vi.used;
|
||||
}
|
||||
mNumIndices += mIndiceCnt[i];
|
||||
}
|
||||
|
||||
int size = mNumIndices * 2;
|
||||
vboIndices = BufferObject.get(GL20.GL_ELEMENT_ARRAY_BUFFER, size);
|
||||
vboIndices.loadBufferData(sbuf.flip(), size);
|
||||
//log.debug("INDEX HITS " + numIndexHits + " / " + sumVertices + " / " + sumIndices);
|
||||
|
||||
GL20 GL = GLAdapter.get();
|
||||
GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
offset = vertexBuffer.position() * 2;
|
||||
|
||||
// upload vertices
|
||||
sbuf.clear();
|
||||
for (VertexItem vi = mVertices; vi != null; vi = vi.next)
|
||||
sbuf.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);
|
||||
vertexBuffer.put(vi.vertices, 0, vi.used);
|
||||
|
||||
clear();
|
||||
compiled = true;
|
||||
|
||||
mClipper = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
mClipper = null;
|
||||
|
||||
if (mVertexMap != null) {
|
||||
vertexPool.releaseAll(mVertexMap.releaseItems());
|
||||
mVertexMap = vertexMapPool.release(mVertexMap);
|
||||
mVertexMap = null;
|
||||
}
|
||||
|
||||
if (compiled) {
|
||||
vboIndices = BufferObject.release(vboIndices);
|
||||
vboVertices = BufferObject.release(vboVertices);
|
||||
} else {
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int i = 0; i <= IND_MESH; i++)
|
||||
mIndices[i] = VertexItem.pool.releaseAll(mIndices[i]);
|
||||
mIndices = null;
|
||||
|
||||
mVertices = VertexItem.pool.releaseAll(mVertices);
|
||||
}
|
||||
}
|
||||
|
||||
public ExtrusionLayer next() {
|
||||
return (ExtrusionLayer) next;
|
||||
}
|
||||
}
|
||||
|
@ -28,9 +28,9 @@ public abstract class RenderElement extends Inlist<RenderElement> {
|
||||
public final static int TEXLINE = 1;
|
||||
public final static int POLYGON = 2;
|
||||
public final static int MESH = 3;
|
||||
public final static int SYMBOL = 4;
|
||||
public final static int BITMAP = 5;
|
||||
public final static int EXTRUSION = 6;
|
||||
public final static int EXTRUSION = 4;
|
||||
public final static int SYMBOL = 5;
|
||||
public final static int BITMAP = 6;
|
||||
|
||||
public final int type;
|
||||
|
||||
@ -73,4 +73,8 @@ public abstract class RenderElement extends Inlist<RenderElement> {
|
||||
* For all other types it is the byte offset in vbo.
|
||||
*/
|
||||
protected int offset;
|
||||
|
||||
protected void compile(ShortBuffer vertexBuffer, ShortBuffer indexBuffer) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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 );"
|
||||
+ "}";
|
||||
|
||||
}
|
@ -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;"
|
||||
+ "}";
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -250,6 +250,71 @@ public abstract class PbfDecoder implements ITileDecoder {
|
||||
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) {
|
||||
return ((val >>> 1) ^ -(val & 1));
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package org.oscim.tiling.source.oscimap4;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.oscim.core.GeometryBuffer.GeometryType;
|
||||
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_POLY = 22;
|
||||
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_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_INDEX = 12;
|
||||
private static final int TAG_ELEM_COORDS = 13;
|
||||
@ -66,6 +68,8 @@ public class TileDecoder extends PbfDecoder {
|
||||
private final TagSet mTileTags;
|
||||
private ITileDataSink mMapDataSink;
|
||||
|
||||
private int mVersion;
|
||||
|
||||
// scale coordinates to tile size
|
||||
private final static float REF_TILE_SIZE = 4096.0f;
|
||||
private final float mScaleFactor = REF_TILE_SIZE / Tile.SIZE;
|
||||
@ -86,7 +90,6 @@ public class TileDecoder extends PbfDecoder {
|
||||
mMapDataSink = sink;
|
||||
|
||||
mTileTags.clearAndNullTags();
|
||||
int version = -1;
|
||||
|
||||
int val;
|
||||
int numTags = 0;
|
||||
@ -108,6 +111,7 @@ public class TileDecoder extends PbfDecoder {
|
||||
case TAG_TILE_LINE:
|
||||
case TAG_TILE_POLY:
|
||||
case TAG_TILE_POINT:
|
||||
case TAG_TILE_MESH:
|
||||
decodeTileElement(tag);
|
||||
break;
|
||||
|
||||
@ -159,8 +163,8 @@ public class TileDecoder extends PbfDecoder {
|
||||
break;
|
||||
|
||||
case TAG_TILE_VERSION:
|
||||
version = decodeVarint32();
|
||||
if (version != 4) {
|
||||
int version = decodeVarint32();
|
||||
if (version < 4 || mVersion > 5) {
|
||||
log.debug("{} invalid version:{}",
|
||||
mTile, version);
|
||||
return false;
|
||||
@ -226,18 +230,19 @@ public class TileDecoder extends PbfDecoder {
|
||||
return true;
|
||||
}
|
||||
|
||||
private int decodeWayIndices(int indexCnt) throws IOException {
|
||||
private int decodeWayIndices(int indexCnt, boolean shift) throws IOException {
|
||||
mElem.ensureIndexSize(indexCnt, false);
|
||||
decodeVarintArray(indexCnt, mElem.index);
|
||||
|
||||
short[] index = mElem.index;
|
||||
int coordCnt = 0;
|
||||
|
||||
if (shift) {
|
||||
for (int i = 0; i < indexCnt; i++) {
|
||||
coordCnt += index[i];
|
||||
index[i] *= 2;
|
||||
}
|
||||
|
||||
}
|
||||
// set end marker
|
||||
if (indexCnt < index.length)
|
||||
index[indexCnt] = -1;
|
||||
@ -248,7 +253,6 @@ public class TileDecoder extends PbfDecoder {
|
||||
private boolean decodeTileElement(int type) throws IOException {
|
||||
|
||||
int bytes = decodeVarint32();
|
||||
short[] index = null;
|
||||
|
||||
int end = position() + bytes;
|
||||
int numIndices = 1;
|
||||
@ -287,8 +291,17 @@ public class TileDecoder extends PbfDecoder {
|
||||
numTags = decodeVarint32();
|
||||
break;
|
||||
|
||||
case TAG_ELEM_NUM_COORDINATES:
|
||||
coordCnt = decodeVarint32();
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
case TAG_ELEM_COORDS:
|
||||
@ -296,6 +309,18 @@ public class TileDecoder extends PbfDecoder {
|
||||
log.debug("{} no coordinates", mTile);
|
||||
}
|
||||
|
||||
if (type == TAG_TILE_MESH) {
|
||||
mElem.ensurePointSize((coordCnt * 3 / 2), false);
|
||||
int cnt = decodeInterleavedPoints3D(mElem.points, 1);
|
||||
|
||||
if (cnt != (3 * coordCnt)) {
|
||||
log.debug("{} wrong number of coordintes {}/{}", mTile,
|
||||
Integer.valueOf(coordCnt),
|
||||
Integer.valueOf(cnt));
|
||||
fail = true;
|
||||
}
|
||||
mElem.pointPos = cnt;
|
||||
} else {
|
||||
mElem.ensurePointSize(coordCnt, false);
|
||||
int cnt = decodeInterleavedPoints(mElem, mScaleFactor);
|
||||
|
||||
@ -305,6 +330,7 @@ public class TileDecoder extends PbfDecoder {
|
||||
Integer.valueOf(cnt));
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TAG_ELEM_LAYER:
|
||||
@ -317,9 +343,8 @@ public class TileDecoder extends PbfDecoder {
|
||||
}
|
||||
|
||||
if (fail || numTags == 0 || numIndices == 0) {
|
||||
log.debug("{} failed: bytes:{} index:{} tags:{} ({},{})",
|
||||
log.debug("{} failed: bytes:{} tags:{} ({},{})",
|
||||
mTile, Integer.valueOf(bytes),
|
||||
Arrays.toString(index),
|
||||
mElem.tags,
|
||||
Integer.valueOf(numIndices),
|
||||
Integer.valueOf(coordCnt));
|
||||
@ -336,6 +361,9 @@ public class TileDecoder extends PbfDecoder {
|
||||
case TAG_TILE_POINT:
|
||||
mElem.type = GeometryType.POINT;
|
||||
break;
|
||||
case TAG_TILE_MESH:
|
||||
mElem.type = GeometryType.TRIS;
|
||||
break;
|
||||
}
|
||||
|
||||
mMapDataSink.process(mElem);
|
||||
|
641
vtm/src/org/oscim/utils/KeyMap.java
Normal file
641
vtm/src/org/oscim/utils/KeyMap.java
Normal 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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user