diff --git a/src/org/oscim/renderer/BaseLayer.java b/src/org/oscim/renderer/BaseLayer.java index 3c2f85a7..5f263ae8 100644 --- a/src/org/oscim/renderer/BaseLayer.java +++ b/src/org/oscim/renderer/BaseLayer.java @@ -31,6 +31,7 @@ import android.opengl.Matrix; * @author Hannes Janetzek */ public class BaseLayer { + private final static String TAG = BaseLayer.class.getName(); // used to not draw a tile twice per frame. private static int mDrawSerial = 0; @@ -47,11 +48,11 @@ public class BaseLayer { } static void draw(MapTile[] tiles, int tileCnt, MapPosition pos) { - + //long start = SystemClock.uptimeMillis(); Matrix.multiplyMM(mVPMatrix, 0, mfProjMatrix, 0, pos.viewMatrix, 0); GLES20.glEnable(GL_POLYGON_OFFSET_FILL); - + LineRenderer.beginLines(); for (int i = 0; i < tileCnt; i++) { MapTile t = tiles[i]; if (t.isVisible && t.state == STATE_READY) @@ -67,10 +68,12 @@ public class BaseLayer { if (t.isVisible && (t.state != STATE_READY) && (t.holder == null)) drawProxyTile(t, pos); } + LineRenderer.endLines(); GLES20.glDisable(GL_POLYGON_OFFSET_FILL); - //GLES20.glFinish() - //Log.d(TAG, "building took " + (end - start)); + //GLES20.glFinish(); + //long end = SystemClock.uptimeMillis(); + //Log.d(TAG, "base took " + (end - start)); mDrawSerial++; } diff --git a/src/org/oscim/renderer/LineRenderer.java b/src/org/oscim/renderer/LineRenderer.java index e6c12a7f..47e02678 100644 --- a/src/org/oscim/renderer/LineRenderer.java +++ b/src/org/oscim/renderer/LineRenderer.java @@ -25,6 +25,9 @@ import static android.opengl.GLES20.glUniformMatrix4fv; import static android.opengl.GLES20.glUseProgram; import static android.opengl.GLES20.glVertexAttribPointer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + import org.oscim.core.MapPosition; import org.oscim.generator.TileGenerator; import org.oscim.renderer.layer.Layer; @@ -32,7 +35,7 @@ import org.oscim.renderer.layer.LineLayer; import org.oscim.theme.renderinstruction.Line; import org.oscim.utils.GlUtils; -import android.graphics.Paint.Cap; +import android.opengl.GLES20; import android.util.Log; /** @@ -43,17 +46,18 @@ public final class LineRenderer { private final static String TAG = "LineRenderer"; private static final int LINE_VERTICES_DATA_POS_OFFSET = 0; - private static final int LINE_VERTICES_DATA_TEX_OFFSET = 4; + //private static final int LINE_VERTICES_DATA_TEX_OFFSET = 4; // shader handles private static int[] lineProgram = new int[2]; private static int[] hLineVertexPosition = new int[2]; - private static int[] hLineTexturePosition = new int[2]; + //private static int[] hLineTexturePosition = new int[2]; private static int[] hLineColor = new int[2]; private static int[] hLineMatrix = new int[2]; private static int[] hLineScale = new int[2]; private static int[] hLineWidth = new int[2]; private static int[] hLineMode = new int[2]; + private static int mTexID; static boolean init() { lineProgram[0] = GlUtils.createProgram(lineVertexShader, @@ -79,9 +83,55 @@ public final class LineRenderer { hLineVertexPosition[i] = glGetAttribLocation(lineProgram[i], "a_pos"); //hLineTexturePosition[i] = glGetAttribLocation(lineProgram[i], "a_st"); } + + byte[] pixel = new byte[128 * 128]; + + for (int x = 0; x < 128; x++) { + float xx = x * x; + for (int y = 0; y < 128; y++) { + float yy = y * y; + int color = (int) (Math.sqrt(xx + yy) * 2); + if (color > 255) + color = 255; + pixel[x + y * 128] = (byte) color; + //pixel[(127 - x) + (127 - y) * 128] = (byte) color; + } + } + + int[] textureIds = new int[1]; + GLES20.glGenTextures(1, textureIds, 0); + mTexID = textureIds[0]; + + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexID); + + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, + GLES20.GL_NEAREST); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, + GLES20.GL_NEAREST); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, + GLES20.GL_MIRRORED_REPEAT); // Set U Wrapping + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, + GLES20.GL_MIRRORED_REPEAT); // Set V Wrapping + + ByteBuffer buf = ByteBuffer.allocateDirect(128 * 128).order(ByteOrder.nativeOrder()); + buf.put(pixel); + buf.position(0); + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_ALPHA, 128, 128, 0, GLES20.GL_ALPHA, + GLES20.GL_UNSIGNED_BYTE, buf); + + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + return true; } + public static void beginLines() { + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexID); + } + + public static void endLines() { + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + } + public static Layer draw(MapPosition pos, Layer layer, float[] matrix, float div, int mode, int bufferOffset) { @@ -126,12 +176,12 @@ public final class LineRenderer { boolean blur = false; // dont increase scale when max is reached boolean strokeMaxZoom = zoom > TileGenerator.STROKE_MAX_ZOOM_LEVEL; - float width = 1; Layer l = layer; for (; l != null && l.type == Layer.LINE; l = l.next) { LineLayer ll = (LineLayer) l; Line line = ll.line; + float width; if (line.fade != -1 && line.fade > zoom) continue; @@ -143,8 +193,8 @@ public final class LineRenderer { GlUtils.setColor(uLineColor, line.color, alpha); - if (blur && line.blur == 0) { - glUniform1f(uLineScale, pixel); + if (mode == 0 && blur && line.blur == 0) { + glUniform1f(uLineScale, 0); blur = false; } @@ -161,12 +211,15 @@ public final class LineRenderer { glUniform1f(uLineWidth, width); if (line.blur != 0) { - blurScale = (ll.width + o.width) / s - (line.blur / s); + //blurScale = (ll.width + o.width) / s - (line.blur / s); + blurScale = 1 - (line.blur / s); glUniform1f(uLineScale, blurScale); blur = true; + } else if (mode == 1) { + glUniform1f(uLineScale, pixel / width); } - if (o.line.cap == Cap.ROUND) { + if (o.roundCap) { if (lineMode != 1) { lineMode = 1; glUniform1i(uLineMode, lineMode); @@ -191,12 +244,15 @@ public final class LineRenderer { glUniform1f(uLineWidth, width); if (line.blur != 0) { - blurScale = (ll.width / lineScale) * line.blur; + //blurScale = (ll.width / lineScale) * line.blur; + blurScale = line.blur; glUniform1f(uLineScale, blurScale); blur = true; + } else if (mode == 1) { + glUniform1f(uLineScale, pixel / width); } - if (line.cap == Cap.ROUND) { + if (ll.roundCap) { if (lineMode != 1) { lineMode = 1; glUniform1i(uLineMode, lineMode); @@ -219,61 +275,125 @@ public final class LineRenderer { + "uniform float u_width;" + "attribute vec4 a_pos;" + "uniform int u_mode;" - //+ "attribute vec2 a_st;" + "varying vec2 v_st;" + "const float dscale = 8.0/2048.0;" + "void main() {" // scale extrusion to u_width pixel // just ignore the two most insignificant bits of a_st :) - + " vec2 dir = dscale * u_width * a_pos.zw;" - + " gl_Position = u_mvp * vec4(a_pos.xy + dir, 0.0, 1.0);" + + " vec2 dir = a_pos.zw;" + + " gl_Position = u_mvp * vec4(a_pos.xy + (dscale * u_width * dir), 0.0, 1.0);" // last two bits of a_st hold the texture coordinates - + " v_st = u_width * (abs(mod(a_pos.zw,4.0)) - 1.0);" - // use bit operations when available (gles 1.3) - // + " v_st = u_width * vec2(a_st.x & 3 - 1, a_st.y & 3 - 1);" + // ..maybe one could wrap texture so that `abs` is not required + + " v_st = abs(mod(dir, 4.0)) - 1.0;" + "}"; private final static String lineSimpleFragmentShader = "" + "precision mediump float;" - + "uniform float u_wscale;" - + "uniform float u_width;" + + "uniform sampler2D tex;" + "uniform int u_mode;" + + "uniform float u_width;" + + "uniform float u_wscale;" + "uniform vec4 u_color;" + "varying vec2 v_st;" + "void main() {" + " float len;" + " if (u_mode == 0)" - + " len = u_width - abs(v_st.s);" - + " else " - + " len = u_width - length(v_st);" - // fade to alpha. u_wscale is the width in pixel which should be - // faded, u_width - len the position of this fragment on the - // perpendicular to this line segment. this only works with no - // perspective - //+ " gl_FragColor = u_color * min(1.0, len / u_wscale);" - + " gl_FragColor = u_color * smoothstep(0.0, u_wscale, len);" - + + " len = abs(v_st.s);" + + " else" + + " len = texture2D(tex, v_st).a;" + // interpolate alpha between: 0.0 < 1.0 - len < u_wscale + // where wscale is 'filter width' / 'line width' and 0 <= len <= sqrt(2) + + " gl_FragColor = u_color * smoothstep(0.0, u_wscale, 1.0 - len);" + //+ " gl_FragColor = u_color * min(1.0, (1.0 - len) / u_wscale);" + "}"; private final static String lineFragmentShader = "" + "#extension GL_OES_standard_derivatives : enable\n" + "precision mediump float;" - + "uniform float u_wscale;" - + "uniform float u_width;" + + "uniform sampler2D tex;" + "uniform int u_mode;" + "uniform vec4 u_color;" + + "uniform float u_width;" + + "uniform float u_wscale;" + "varying vec2 v_st;" + "void main() {" + " float len;" + " float fuzz;" + " if (u_mode == 0){" - + " len = u_width - abs(v_st.s);" - + " fuzz = u_wscale + fwidth(v_st.s);" + + " len = abs(v_st.s);" + + " fuzz = fwidth(v_st.s);" + " } else {" - + " len = u_width - length(v_st);" + + " len = texture2D(tex, v_st).a;" + //+ " len = length(v_st);" + " vec2 st_width = fwidth(v_st);" - + " fuzz = u_wscale + max(st_width.s, st_width.t);" + + " fuzz = max(st_width.s, st_width.t);" + " }" - + " gl_FragColor = u_color * min(1.0, len / fuzz);" + // smoothstep is too sharp, guess one could increase extrusion with z.. + // but this looks ok too: + + " gl_FragColor = u_color * min(1.0, (1.0 - len) / (u_wscale + fuzz));" + //+ " gl_FragColor = u_color * smoothstep(0.0, fuzz + u_wscale, 1.0 - len);" + "}"; + + // private final static String lineVertexShader = "" + // + "precision mediump float;" + // + "uniform mat4 u_mvp;" + // + "uniform float u_width;" + // + "attribute vec4 a_pos;" + // + "uniform int u_mode;" + // //+ "attribute vec2 a_st;" + // + "varying vec2 v_st;" + // + "const float dscale = 8.0/2048.0;" + // + "void main() {" + // // scale extrusion to u_width pixel + // // just ignore the two most insignificant bits of a_st :) + // + " vec2 dir = a_pos.zw;" + // + " gl_Position = u_mvp * vec4(a_pos.xy + (dscale * u_width * dir), 0.0, 1.0);" + // // last two bits of a_st hold the texture coordinates + // + " v_st = u_width * (abs(mod(dir, 4.0)) - 1.0);" + // // use bit operations when available (gles 1.3) + // // + " v_st = u_width * vec2(a_st.x & 3 - 1, a_st.y & 3 - 1);" + // + "}"; + // + // private final static String lineSimpleFragmentShader = "" + // + "precision mediump float;" + // + "uniform float u_wscale;" + // + "uniform float u_width;" + // + "uniform int u_mode;" + // + "uniform vec4 u_color;" + // + "varying vec2 v_st;" + // + "void main() {" + // + " float len;" + // + " if (u_mode == 0)" + // + " len = abs(v_st.s);" + // + " else " + // + " len = length(v_st);" + // // fade to alpha. u_wscale is the width in pixel which should be + // // faded, u_width - len the position of this fragment on the + // // perpendicular to this line segment. this only works with no + // // perspective + // //+ " gl_FragColor = min(1.0, (u_width - len) / u_wscale) * u_color;" + // + " gl_FragColor = u_color * smoothstep(0.0, u_wscale, (u_width - len));" + // + "}"; + // + // private final static String lineFragmentShader = "" + // + "#extension GL_OES_standard_derivatives : enable\n" + // + "precision mediump float;" + // + "uniform float u_wscale;" + // + "uniform float u_width;" + // + "uniform int u_mode;" + // + "uniform vec4 u_color;" + // + "varying vec2 v_st;" + // + "void main() {" + // + " float len;" + // + " float fuzz;" + // + " if (u_mode == 0){" + // + " len = abs(v_st.s);" + // + " fuzz = u_wscale + fwidth(v_st.s);" + // + " } else {" + // + " len = length(v_st);" + // + " vec2 st_width = fwidth(v_st);" + // + " fuzz = u_wscale + max(st_width.s, st_width.t);" + // + " }" + // + " gl_FragColor = u_color * min(1.0, (u_width - len) / fuzz);" + // + "}"; } diff --git a/src/org/oscim/renderer/layer/LineLayer.java b/src/org/oscim/renderer/layer/LineLayer.java index 61f9532c..51dc0f13 100644 --- a/src/org/oscim/renderer/layer/LineLayer.java +++ b/src/org/oscim/renderer/layer/LineLayer.java @@ -35,6 +35,8 @@ public final class LineLayer extends Layer { public Line line; public float width; + public boolean roundCap; + LineLayer(int layer) { this.layer = layer; this.type = Layer.LINE; @@ -51,11 +53,11 @@ public final class LineLayer extends Layer { /** * line extrusion is based on code from GLMap - * (https://github.com/olofsj/GLMap/) by olofsj + * (https://github.com/olofsj/GLMap/) * @param points - * array of points as float x_n = i, y_n = i+1 + * array of points as x,y pairs * @param index - * array of line indices holding the length of the individual + * array of indices holding the length of the individual * lines * @param closed * whether to connect start- and end-point @@ -87,6 +89,21 @@ public final class LineLayer extends Layer { if (!MapView.enableClosePolygons) closed = false; + // Note: just a hack to save some vertices, when there are more than 200 lines + // per type + if (rounded) { + int cnt = 0; + for (int i = 0, n = index.length; i < n; i++, cnt++) { + if (index[i] < 0) + break; + if (cnt > 400) { + rounded = false; + break; + } + } + } + roundCap = rounded; + for (int i = 0, pos = 0, n = index.length; i < n; i++) { int length = index[i]; @@ -95,10 +112,6 @@ public final class LineLayer extends Layer { if (length < 0) break; - // Note: just a hack to save some vertices - if (rounded && i > 200) - rounded = false; - // need at least two points if (length < 4) { pos += length;