diff --git a/vtm-gdx-html/src/org/oscim/gdx/client/GwtCanvas.java b/vtm-gdx-html/src/org/oscim/gdx/client/GwtCanvas.java index a763e27b..a5019a68 100644 --- a/vtm-gdx-html/src/org/oscim/gdx/client/GwtCanvas.java +++ b/vtm-gdx-html/src/org/oscim/gdx/client/GwtCanvas.java @@ -9,6 +9,7 @@ import com.google.gwt.canvas.dom.client.Context2d.LineJoin; public class GwtCanvas implements org.oscim.backend.canvas.Canvas { GwtBitmap bitmap; + public GwtCanvas() { // canvas comes with gdx pixmap } @@ -16,38 +17,38 @@ public class GwtCanvas implements org.oscim.backend.canvas.Canvas { @Override public void setBitmap(Bitmap bitmap) { this.bitmap = (GwtBitmap) bitmap; - //this.bitmap.pixmap.setColor(0x00ffffff); - this.bitmap.pixmap.getContext().clearRect(0, 0, this.bitmap.getWidth(), this.bitmap.getHeight()); + Context2d ctx = this.bitmap.pixmap.getContext(); + + ctx.clearRect(0, 0, this.bitmap.getWidth(), this.bitmap.getHeight()); + ctx.setLineJoin(LineJoin.BEVEL); } @Override public void drawText(String string, float x, float y, Paint paint) { - if (bitmap == null){ - Log.d("", "no bitmap set on canvas"); + if (bitmap == null) { + Log.d("BUG", "no bitmap set"); + return; } GwtPaint p = (GwtPaint) paint; + + if (p.stroke && GwtCanvasAdapter.NO_STROKE_TEXT) + return; + Context2d ctx = bitmap.pixmap.getContext(); ctx.setFont(p.font); - ctx.setLineJoin(LineJoin.ROUND); - - if (p.stroke){ - //Log.d("", "stroke " + p.stroke + " " + p.color + " " + p.font + " "+ string); + + if (p.stroke) { ctx.setLineWidth(p.strokeWidth); ctx.setStrokeStyle(p.color); - //ctx.strokeText(string, p.strokeWidth + x, p.strokeWidth + y); - ctx.strokeText(string, x + 1, y + 1); - } else{ - //Log.d("", "fill " + p.stroke + " " + p.color + " " + p.font + " "+ string); + ctx.strokeText(string, (int) (x + 1), (int) (y + 1)); + } else { ctx.setFillStyle(p.color); - ctx.fillText(string, x + 1, y + 1); + ctx.fillText(string, (int) (x + 1), (int) (y + 1)); } } @Override public void drawBitmap(Bitmap bitmap, float x, float y) { - // TODO Auto-generated method stub - } - } diff --git a/vtm-gdx-html/src/org/oscim/gdx/client/GwtCanvasAdapter.java b/vtm-gdx-html/src/org/oscim/gdx/client/GwtCanvasAdapter.java index 7913d450..bfac9bf9 100644 --- a/vtm-gdx-html/src/org/oscim/gdx/client/GwtCanvasAdapter.java +++ b/vtm-gdx-html/src/org/oscim/gdx/client/GwtCanvasAdapter.java @@ -12,8 +12,11 @@ import com.google.gwt.canvas.dom.client.TextMetrics; public class GwtCanvasAdapter extends CanvasAdapter { + public static boolean NO_STROKE_TEXT = false; + public static final GwtCanvasAdapter INSTANCE = new GwtCanvasAdapter(); static final Context2d ctx; + static { Canvas canvas = Canvas.createIfSupported(); canvas.setCoordinateSpaceWidth(1); @@ -24,7 +27,7 @@ public class GwtCanvasAdapter extends CanvasAdapter { static synchronized float getTextWidth(String text, String font) { ctx.setFont(font); TextMetrics tm = ctx.measureText(text); - return (float)tm.getWidth(); + return (float) tm.getWidth(); } @Override diff --git a/vtm-gdx-html/src/org/oscim/gdx/client/GwtLauncher.java b/vtm-gdx-html/src/org/oscim/gdx/client/GwtLauncher.java index ca7b3d81..4ff43553 100644 --- a/vtm-gdx-html/src/org/oscim/gdx/client/GwtLauncher.java +++ b/vtm-gdx-html/src/org/oscim/gdx/client/GwtLauncher.java @@ -4,18 +4,32 @@ package org.oscim.gdx.client; import org.oscim.backend.CanvasAdapter; import org.oscim.backend.GL20; import org.oscim.backend.GLAdapter; +import org.oscim.backend.Log; import org.oscim.core.MapPosition; +import org.oscim.core.MercatorProjection; import org.oscim.core.Tile; import org.oscim.gdx.GdxMap; +import org.oscim.layers.Layer; +import org.oscim.layers.tile.bitmap.BitmapTileLayer; +import org.oscim.layers.tile.bitmap.NaturalEarth; +import org.oscim.renderer.GLRenderer; import org.oscim.tilesource.TileSource; import org.oscim.tilesource.oscimap2.OSciMap2TileSource; import org.oscim.tilesource.oscimap4.OSciMap4TileSource; +import org.oscim.view.MapView; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.backends.gwt.GwtApplication; import com.badlogic.gdx.backends.gwt.GwtApplicationConfiguration; import com.badlogic.gdx.backends.gwt.GwtGraphics; +import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderCallback; +import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderState; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.DockLayoutPanel; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.RootPanel; public class GwtLauncher extends GwtApplication { @@ -24,6 +38,24 @@ public class GwtLauncher extends GwtApplication { GwtApplicationConfiguration cfg = new GwtApplicationConfiguration( GwtGraphics.getWindowWidthJSNI(), GwtGraphics.getWindowHeightJSNI()); + + DockLayoutPanel p = new DockLayoutPanel(Unit.EM); + p.setHeight("100%"); + p.setWidth("100%"); + + RootPanel.get().add(p); + + //HTML header = new HTML("header"); + //p.addNorth(header, 2); + //header.setStyleName("header"); + + //HTML footer = new HTML("footer"); + //footer.setStyleName("footer"); + //p.addSouth(footer, 2); + + cfg.rootPanel = new FlowPanel(); + p.add(cfg.rootPanel); + cfg.stencil = true; cfg.fps = 30; @@ -40,6 +72,21 @@ public class GwtLauncher extends GwtApplication { return new GwtGdxMap(); } + @Override + public PreloaderCallback getPreloaderCallback() { + return new PreloaderCallback() { + + @Override + public void update(PreloaderState state) { + } + + @Override + public void error(String file) { + Log.d(this.getClass().getName(), "error loading " + file); + } + }; + } + private static native String getMapConfig(String key)/*-{ return $wnd.mapconfig && $wnd.mapconfig[key] || null; }-*/; @@ -48,34 +95,112 @@ public class GwtLauncher extends GwtApplication { @Override public void create() { - CanvasAdapter.g = GwtCanvasAdapter.INSTANCE; - GLAdapter.g = (GL20)Gdx.graphics.getGL20(); - GLAdapter.GDX_WEBGL_QUIRKS = true; + // stroke text takes about 70% cpu time in firefox: + // https://bug568526.bugzilla.mozilla.org/attachment.cgi?id=447932 + // <- circle/stroke test 800ms firefox, 80ms chromium.. + // TODO use texture atlas to avoid drawing text-textures + if (GwtApplication.agentInfo().isLinux() && GwtApplication.agentInfo().isFirefox()) + GwtCanvasAdapter.NO_STROKE_TEXT = true; - //GLAdapter.NON_PREMUL_CANVAS = true; + CanvasAdapter.g = GwtCanvasAdapter.INSTANCE; + GLAdapter.g = (GL20) Gdx.graphics.getGL20(); + GLAdapter.GDX_WEBGL_QUIRKS = true; + GLRenderer.setBackgroundColor(0xffffff); //Gdx.app.setLogLevel(Application.LOG_DEBUG); super.create(); - double lat = Double.parseDouble(getMapConfig("latitude")); - double lon = Double.parseDouble(getMapConfig("longitude")); - int zoom = Integer.parseInt(getMapConfig("zoom")); + double lat = Double.parseDouble(getMapConfig("latitude")); + double lon = Double.parseDouble(getMapConfig("longitude")); + int zoom = Integer.parseInt(getMapConfig("zoom")); + float tilt = 0; + float rotation = 0; + if (Window.Location.getHash() != null) { + String hash = Window.Location.getHash(); + //Log.d("...", "hash: " + hash); + hash = hash.substring(1); + String[] pairs = hash.split(","); + + for (String p : pairs) { + try { + if (p.startsWith("lat=")) + lat = Double.parseDouble(p.substring(4)); + else if (p.startsWith("lon=")) + lon = Double.parseDouble(p.substring(4)); + else if (p.startsWith("scale=")) + zoom = Integer.parseInt(p.substring(6)); + else if (p.startsWith("rot=")) + rotation = Float.parseFloat(p.substring(4)); + else if (p.startsWith("tilt=")) + tilt = Float.parseFloat(p.substring(5)); + } catch (NumberFormatException e) { + + } + } + } MapPosition p = new MapPosition(); p.setZoomLevel(zoom); p.setPosition(lat, lon); mMapView.setMapPosition(p); + mMapView.getMapViewPosition().setTilt(tilt); + mMapView.getMapViewPosition().setRotation(rotation); String url = getMapConfig("tileurl"); + TileSource tileSource; if ("oscimap4".equals(getMapConfig("tilesource"))) tileSource = new OSciMap4TileSource(); else tileSource = new OSciMap2TileSource(); - tileSource.setOption("url", url); - mMapView.setBaseMap(tileSource); + + initDefaultMap(tileSource, false, true, true); + + mMapView.getLayerManager().add(new UrlPositionUpdate(mMapView)); + mMapView.setBackgroundMap(new BitmapTileLayer(mMapView, NaturalEarth.INSTANCE)); + + } + } + + class UrlPositionUpdate extends Layer { + + public UrlPositionUpdate(MapView mapView) { + super(mapView); + } + + private int curLon, curLat, curZoom, curTilt, curRot; + + @Override + public void onUpdate(MapPosition pos, boolean changed, boolean clear) { + if (!changed) + return; + int lat = (int) (MercatorProjection.toLatitude(pos.y) * 1000); + int lon = (int) (MercatorProjection.toLongitude(pos.x) * 1000); + int rot = (int) (pos.angle); + rot = (int) (pos.angle) % 360; + rot = rot < 0 ? -rot : rot; + + if (curZoom != pos.zoomLevel + || curLat != lat + || curLon != lon + || curTilt != rot + || curRot != (int) (pos.angle)) { + + curLat = lat; + curLon = lon; + curZoom = pos.zoomLevel; + curTilt = (int) pos.tilt; + curRot = rot; + + //String newURL = Window.Location.createUrlBuilder() + // .setHash("scale=" + pos.zoomLevel + ",rot=" + curRot + ",tilt=" + + // curTilt + ",lat=" + (curLat / 1000f) + ",lon=" + (curLon / 1000f)) + // .buildString(); + //Window.Location.replace(newURL); + //Window.Location. + } } } } diff --git a/vtm-gdx-html/src/org/oscim/gdx/emu/com/badlogic/gdx/backends/gwt/GwtApplication.java b/vtm-gdx-html/src/org/oscim/gdx/emu/com/badlogic/gdx/backends/gwt/GwtApplication.java index ac14dda5..c9b85bc3 100644 --- a/vtm-gdx-html/src/org/oscim/gdx/emu/com/badlogic/gdx/backends/gwt/GwtApplication.java +++ b/vtm-gdx-html/src/org/oscim/gdx/emu/com/badlogic/gdx/backends/gwt/GwtApplication.java @@ -16,6 +16,8 @@ package com.badlogic.gdx.backends.gwt; +import org.oscim.backend.GL20; + import com.badlogic.gdx.Application; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Audio; @@ -43,7 +45,6 @@ import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.HasHorizontalAlignment; import com.google.gwt.user.client.ui.HasVerticalAlignment; -import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.InlineHTML; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Panel; @@ -130,7 +131,6 @@ public abstract class GwtApplication implements EntryPoint, Application { private void setupLoop() { // setup modules - consoleLog("setupLoop"); try { graphics = new GwtGraphics(root, config); } catch (Throwable e) { @@ -167,10 +167,17 @@ public abstract class GwtApplication implements EntryPoint, Application { throw new RuntimeException(t); } + final int frameTime = (int) ((1f / config.fps) * 1000); + + Gdx.gl.glClearColor(1, 1, 1, 1); + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); + // setup rendering timer new Timer() { + //long lastRun; @Override public void run() { + //long startRender = 0; try { graphics.update(); if (graphics.getWidth() != lastWidth @@ -188,14 +195,26 @@ public abstract class GwtApplication implements EntryPoint, Application { runnablesHelper.get(i).run(); } runnablesHelper.clear(); + //startRender = System.currentTimeMillis(); listener.render(); input.justTouched = false; } catch (Throwable t) { error("GwtApplication", "exception: " + t.getMessage(), t); throw new RuntimeException(t); } + + long now = System.currentTimeMillis(); + int diff = (int)(now - graphics.lastTimeStamp); + + //if (diff > 80) + // consoleLog("diff " + diff + " " + (now - startRender) + " " + graphics.getFramesPerSecond() ); + + diff = frameTime - diff; + //lastRun = now; + + this.schedule(diff > 5 ? diff : 5); } - }.scheduleRepeating((int) ((1f / config.fps) * 1000)); + }.schedule(0); //scheduleRepeating((int) ((1f / config.fps) * 1000)); } public Panel getRootPanel() { @@ -207,9 +226,11 @@ public abstract class GwtApplication implements EntryPoint, Application { public PreloaderCallback getPreloaderCallback() { final Panel preloaderPanel = new VerticalPanel(); preloaderPanel.setStyleName("gdx-preloader"); - final Image logo = new Image(GWT.getModuleBaseURL() + "logo.png"); - logo.setStyleName("logo"); - preloaderPanel.add(logo); + + //final Image logo = new Image(GWT.getModuleBaseURL() + "logo.png"); + //logo.setStyleName("logo"); + //preloaderPanel.add(logo); + final Panel meterPanel = new SimplePanel(); meterPanel.setStyleName("gdx-meter"); meterPanel.addStyleName("red"); diff --git a/vtm-gdx-html/src/org/oscim/gdx/emu/com/badlogic/gdx/backends/gwt/GwtGraphics.java b/vtm-gdx-html/src/org/oscim/gdx/emu/com/badlogic/gdx/backends/gwt/GwtGraphics.java index af98dfb5..8d5821a7 100644 --- a/vtm-gdx-html/src/org/oscim/gdx/emu/com/badlogic/gdx/backends/gwt/GwtGraphics.java +++ b/vtm-gdx-html/src/org/oscim/gdx/emu/com/badlogic/gdx/backends/gwt/GwtGraphics.java @@ -47,7 +47,7 @@ public class GwtGraphics implements Graphics { boolean inFullscreenMode = false; double pixelRatio; - public GwtGraphics(Panel root, GwtApplicationConfiguration config) { + public GwtGraphics(Panel root, final GwtApplicationConfiguration config) { Canvas canvasWidget = Canvas.createIfSupported(); if (canvasWidget == null) throw new GdxRuntimeException("Canvas not supported"); @@ -61,7 +61,6 @@ public class GwtGraphics implements Graphics { canvas.getStyle().setWidth(config.width, Unit.PX); canvas.getStyle().setHeight(config.height, Unit.PX); - this.config = config; WebGLContextAttributes attributes = WebGLContextAttributes.create(); @@ -72,17 +71,18 @@ public class GwtGraphics implements Graphics { context = WebGLRenderingContext.getContext(canvas, attributes); context.viewport(0, 0, config.width, config.height); - - // this actually *enables* the option to use std derivatives in shader.. + + // this actually *enables* the option to use std derivatives in shader.. context.getExtension("OES_standard_derivatives"); - + this.gl = config.useDebugGL ? new GwtGL20Debug(context) : new GwtGL20(context); + canvas.setId("gdx-canvas"); Window.addResizeHandler(new ResizeHandler() { @Override public void onResize(ResizeEvent event) { - int w = getWindowWidthJSNI(); - int h = getWindowHeightJSNI(); + int w = config.rootPanel.getOffsetWidth(); + int h = config.rootPanel.getOffsetHeight(); canvas.getStyle().setWidth(w, Unit.PX); canvas.getStyle().setHeight(h, Unit.PX); diff --git a/vtm-gdx-html/src/org/oscim/gdx/emu/com/badlogic/gdx/backends/gwt/GwtInput.java b/vtm-gdx-html/src/org/oscim/gdx/emu/com/badlogic/gdx/backends/gwt/GwtInput.java index fe986d1d..e8e88f8c 100644 --- a/vtm-gdx-html/src/org/oscim/gdx/emu/com/badlogic/gdx/backends/gwt/GwtInput.java +++ b/vtm-gdx-html/src/org/oscim/gdx/emu/com/badlogic/gdx/backends/gwt/GwtInput.java @@ -382,6 +382,10 @@ public class GwtInput implements Input { .addEventListener( name, function(e) { + if (capture){ + e.preventDefault(); + e.stopPropagation(); + } handler.@com.badlogic.gdx.backends.gwt.GwtInput::handleEvent(Lcom/google/gwt/dom/client/NativeEvent;)(e); }, capture); }-*/; @@ -440,6 +444,7 @@ public class GwtInput implements Input { private void hookEvents() { addEventListener(canvas, "mousedown", this, true); + addEventListener(canvas, "contextmenu", this, true); addEventListener(Document.get(), "mousedown", this, true); addEventListener(canvas, "mouseup", this, true); addEventListener(Document.get(), "mouseup", this, true); diff --git a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/bitmap/BitmapTileLayer.java b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/bitmap/BitmapTileLayer.java index 688f64da..fd336499 100644 --- a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/bitmap/BitmapTileLayer.java +++ b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/bitmap/BitmapTileLayer.java @@ -17,14 +17,17 @@ package org.oscim.layers.tile.bitmap; import java.net.URL; import org.oscim.backend.canvas.Bitmap; +import org.oscim.core.MapPosition; import org.oscim.core.Tile; import org.oscim.gdx.client.GwtBitmap; import org.oscim.layers.tile.MapTile; import org.oscim.layers.tile.TileLayer; import org.oscim.layers.tile.TileLoader; import org.oscim.layers.tile.TileManager; +import org.oscim.layers.tile.bitmap.TileSource.FadeStep; import org.oscim.renderer.sublayers.BitmapLayer; import org.oscim.renderer.sublayers.Layers; +import org.oscim.utils.FastMath; import org.oscim.view.MapView; import com.google.gwt.event.dom.client.ErrorEvent; @@ -37,10 +40,41 @@ import com.google.gwt.user.client.ui.RootPanel; public class BitmapTileLayer extends TileLayer { final TileSource mTileSource; + private final FadeStep[] mFade; public BitmapTileLayer(MapView mapView, TileSource tileSource) { super(mapView, tileSource.getZoomLevelMin(), tileSource.getZoomLevelMax(), 100); mTileSource = tileSource; + mFade = mTileSource.getFadeSteps(); + } + + @Override + public void onUpdate(MapPosition pos, boolean changed, boolean clear) { + super.onUpdate(pos, changed, clear); + + if (mFade == null) { + mRenderLayer.setBitmapAlpha(1); + return; + } + + float alpha = 0; + for (FadeStep f : mFade) { + if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd) + continue; + + if (f.alphaStart == f.alphaEnd) { + alpha = f.alphaStart; + break; + } + double range = f.scaleEnd / f.scaleStart; + float a = (float)((range - (pos.scale / f.scaleStart)) / range); + a = FastMath.clamp(a, 0, 1); + // interpolate alpha between start and end + alpha = a * f.alphaStart + (1 - a) * f.alphaEnd; + break; + } + + mRenderLayer.setBitmapAlpha(alpha); } @Override diff --git a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/tilesource/common/PbfTileDataSource.java b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/tilesource/common/PbfTileDataSource.java index 48201a85..5768e4a8 100644 --- a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/tilesource/common/PbfTileDataSource.java +++ b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/tilesource/common/PbfTileDataSource.java @@ -16,16 +16,12 @@ package org.oscim.tilesource.common; import java.io.IOException; import java.io.InputStream; -import java.net.SocketException; -import java.net.SocketTimeoutException; -import java.net.UnknownHostException; +import org.oscim.backend.Log; import org.oscim.layers.tile.MapTile; import org.oscim.tilesource.ITileDataSink; import org.oscim.tilesource.ITileDataSource; -import org.oscim.backend.Log; - /** * * @@ -50,42 +46,15 @@ public abstract class PbfTileDataSource implements ITileDataSource { mSink = sink; try { mConn.sendRequest(tile, this); - //InputStream is; - // if (!mConn.sendRequest(tile, this)) { - // Log.d(TAG, tile + " Request Failed"); - // result = QueryResult.FAILED; - // } else if ((is = mConn.readHeader()) != null) { - // boolean win = mTileDecoder.decode(tile, sink, is, mConn.getContentLength()); - // if (!win) - // Log.d(TAG, tile + " failed"); - // } else { - // Log.d(TAG, tile + " Network Error"); - // result = QueryResult.FAILED; - // } - // } catch (SocketException e) { - // Log.d(TAG, tile + " Socket exception: " + e.getMessage()); - // result = QueryResult.FAILED; - // } catch (SocketTimeoutException e) { - // Log.d(TAG, tile + " Socket Timeout"); - // result = QueryResult.FAILED; - // } catch (UnknownHostException e) { - // Log.d(TAG, tile + " No Network"); - // result = QueryResult.FAILED; } catch (Exception e) { e.printStackTrace(); result = QueryResult.FAILED; } - //mConn.requestCompleted(); - - //if (result != QueryResult.SUCCESS) - // mConn.close(); - return result; } public void process(InputStream is, int length) { - //Log.d(TAG, mTile + " process " + is + " " + length + " " + mSink); boolean win = false; if (length >= 0) { @@ -100,9 +69,6 @@ public abstract class PbfTileDataSource implements ITileDataSource { mConn.requestCompleted(); mSink.completed(win); - - //mTile = null; - //mSink = null; } @Override diff --git a/vtm-gdx-html/war/index.html b/vtm-gdx-html/war/index.html index 7078b449..02af9c94 100644 --- a/vtm-gdx-html/war/index.html +++ b/vtm-gdx-html/war/index.html @@ -1,68 +1,83 @@ - -vtm-gdx - + + vtm-gdx + - - - - - - + + + + + + - + - + - + -
+ + + - - - - - -