From 7cf4ca27f356a9868012c6ed7de32c79d17f80a0 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Fri, 22 Feb 2013 05:30:24 +0100 Subject: [PATCH] - add initial version of line stipple renderer - 'vbo' moved to 'Layers' --- src/org/oscim/generator/TileGenerator.java | 140 +++++---- src/org/oscim/renderer/BaseMap.java | 36 ++- src/org/oscim/renderer/GLRenderer.java | 36 +-- src/org/oscim/renderer/LineRenderer.java | 39 ++- src/org/oscim/renderer/LineTexRenderer.java | 287 ++++++++++++++++++ src/org/oscim/renderer/MapTile.java | 9 - src/org/oscim/renderer/TileManager.java | 49 +-- .../oscim/renderer/layer/ExtrusionLayer.java | 3 +- src/org/oscim/renderer/layer/Layer.java | 17 +- src/org/oscim/renderer/layer/Layers.java | 187 +++++++----- src/org/oscim/renderer/layer/LineLayer.java | 15 +- .../oscim/renderer/layer/LineTexLayer.java | 229 ++++++++++++++ .../oscim/renderer/layer/PolygonLayer.java | 8 +- .../oscim/renderer/layer/TextureLayer.java | 3 +- .../oscim/renderer/layer/VertexPoolItem.java | 2 +- .../oscim/renderer/overlays/BasicOverlay.java | 35 ++- .../renderer/overlays/TestLineOverlay.java | 267 +++++++++------- .../oscim/renderer/overlays/TestOverlay.java | 96 +++--- .../oscim/renderer/overlays/TextOverlay.java | 4 +- .../renderer/overlays/TextOverlayExp.java | 16 +- .../oscim/theme/renderinstruction/Line.java | 32 +- src/org/oscim/utils/FastMath.java | 7 + src/org/oscim/utils/GlUtils.java | 80 +++-- 23 files changed, 1148 insertions(+), 449 deletions(-) create mode 100644 src/org/oscim/renderer/LineTexRenderer.java create mode 100644 src/org/oscim/renderer/layer/LineTexLayer.java diff --git a/src/org/oscim/generator/TileGenerator.java b/src/org/oscim/generator/TileGenerator.java index 846a0250..fd1084ef 100644 --- a/src/org/oscim/generator/TileGenerator.java +++ b/src/org/oscim/generator/TileGenerator.java @@ -16,6 +16,8 @@ package org.oscim.generator; import static org.oscim.generator.JobTile.STATE_NONE; +import java.util.Arrays; + import org.oscim.core.MercatorProjection; import org.oscim.core.Tag; import org.oscim.core.Tile; @@ -27,6 +29,7 @@ import org.oscim.renderer.layer.ExtrusionLayer; import org.oscim.renderer.layer.Layer; import org.oscim.renderer.layer.Layers; import org.oscim.renderer.layer.LineLayer; +import org.oscim.renderer.layer.LineTexLayer; import org.oscim.renderer.layer.PolygonLayer; import org.oscim.renderer.layer.TextItem; import org.oscim.theme.IRenderCallback; @@ -132,7 +135,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { */ public TileGenerator(MapView mapView) { // mMapView = mapView; - mClipper = new LineClipper(0,0,Tile.TILE_SIZE, Tile.TILE_SIZE, true); + mClipper = new LineClipper(0, 0, Tile.TILE_SIZE, Tile.TILE_SIZE, true); } public void cleanup() { @@ -254,6 +257,8 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { mTagHouseNr = tags[i]; tags[i] = mTagEmptyHouseNr; } else if (mTile.zoomLevel >= 17 && + // FIXME, allow overlays to intercept + // this, or use a theme option for this key == Tag.TAG_KEY_BUILDING) { mRenderBuildingModel = true; } @@ -270,38 +275,12 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { mTagName = null; mTagHouseNr = null; - //if (mMapProjection != null) { - // long x = mTile.pixelX; - // long y = mTile.pixelY + Tile.TILE_SIZE; - // long z = Tile.TILE_SIZE << mTile.zoomLevel; - // - // double divx, divy; - // long dx = (x - (z >> 1)); - // long dy = (y - (z >> 1)); - // - // if (mMapProjection == WebMercator.NAME) { - // double div = WebMercator.f900913 / (z >> 1); - // // divy = f900913 / (z >> 1); - // mPoiX = (float) (longitude / div - dx); - // mPoiY = (float) (latitude / div + dy); - // } else { - // divx = 180000000.0 / (z >> 1); - // divy = z / PIx4; - // mPoiX = (float) (longitude / divx - dx); - // double sinLat = Math.sin(latitude * PI180); - // mPoiY = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy); - // return; - // } - //} else { mPoiX = longitude; mPoiY = latitude; - //} // remove tags that should not be cached in Rendertheme filterTags(tags); - // Log.d(TAG, "renderPointOfInterest: " + mTagName); - // mNodeRenderInstructions = TileGenerator.renderTheme.matchNode(this, tags, mTile.zoomLevel); } @@ -338,8 +317,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { private void debugUnmatched(boolean closed, Tag[] tags) { - Log.d(TAG, "way not matched: " + tags[0] + " " - + (tags.length > 1 ? tags[1] : "") + " " + closed); + Log.d(TAG, "DBG way not matched: " + closed + " " + Arrays.deepToString(tags)); mTagName = new Tag("name", tags[0].key + ":" + tags[0].value, false); @@ -366,37 +344,62 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { // ----------------- RenderThemeCallback ----------------- @Override public void renderWay(Line line, int level) { - // projectToTile(); + // TODO projectToTile(); - if (line.outline && mCurLineLayer == null) { - // TODO fix this in RenderTheme - Log.e(TAG, "theme issue, cannot add outline: line must come before outline!"); - return; - } int numLayer = (mDrawingLayer * 2) + level; - LineLayer lineLayer = (LineLayer) mLayers.getLayer(numLayer, Layer.LINE); - if (lineLayer == null) - return; - - if (lineLayer.line == null) { - lineLayer.line = line; - - float w = line.width; - if (!line.fixed) { - w *= mStrokeScale; - w *= mProjectionScaleFactor; + if (line.stipple == 0) { + if (line.outline && mCurLineLayer == null) { + // FIXME in RenderTheme + Log.e(TAG, "BUG in theme: line must come before outline!"); + return; } - lineLayer.width = w; - } - if (line.outline) { - lineLayer.addOutline(mCurLineLayer); - return; - } + LineLayer lineLayer = (LineLayer) + mLayers.getLayer(numLayer, Layer.LINE); - lineLayer.addLine(mCoords, mIndices, mClosed); - mCurLineLayer = lineLayer; + if (lineLayer == null) + return; + + if (lineLayer.line == null) { + lineLayer.line = line; + + float w = line.width; + if (!line.fixed) { + w *= mStrokeScale; + w *= mProjectionScaleFactor; + } + lineLayer.width = w; + } + + if (line.outline) { + lineLayer.addOutline(mCurLineLayer); + return; + } + + lineLayer.addLine(mCoords, mIndices, mClosed); + mCurLineLayer = lineLayer; + } else { + LineTexLayer lineLayer = (LineTexLayer) + mLayers.getLayer(numLayer, Layer.TEXLINE); + + if (lineLayer == null) + return; + + if (lineLayer.line == null) { + lineLayer.line = line; + + float w = line.width; + if (!line.fixed) { + w *= mStrokeScale; + w *= mProjectionScaleFactor; + } + lineLayer.width = w; + } + + + lineLayer.addLine(mCoords, mIndices); + } } @Override @@ -480,8 +483,8 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { int length = mIndices[i]; if (length < 4) break; - mLabels = WayDecorator.renderText(mClipper,mCoords, mTagName.value, text, - offset, length, mLabels); + mLabels = WayDecorator.renderText(mClipper, mCoords, mTagName.value, text, + offset, length, mLabels); offset += length; } } @@ -520,6 +523,31 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { // // TODO move this to Projection classes // + + //if (mMapProjection != null) { + // long x = mTile.pixelX; + // long y = mTile.pixelY + Tile.TILE_SIZE; + // long z = Tile.TILE_SIZE << mTile.zoomLevel; + // + // double divx, divy; + // long dx = (x - (z >> 1)); + // long dy = (y - (z >> 1)); + // + // if (mMapProjection == WebMercator.NAME) { + // double div = WebMercator.f900913 / (z >> 1); + // // divy = f900913 / (z >> 1); + // mPoiX = (float) (longitude / div - dx); + // mPoiY = (float) (latitude / div + dy); + // } else { + // divx = 180000000.0 / (z >> 1); + // divy = z / PIx4; + // mPoiX = (float) (longitude / divx - dx); + // double sinLat = Math.sin(latitude * PI180); + // mPoiY = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy); + // return; + // } + //} else { + // private String mMapProjection; // private static final double PI180 = (Math.PI / 180) / 1000000.0; // private static final double PIx4 = Math.PI * 4; diff --git a/src/org/oscim/renderer/BaseMap.java b/src/org/oscim/renderer/BaseMap.java index dcb3a446..45205c77 100644 --- a/src/org/oscim/renderer/BaseMap.java +++ b/src/org/oscim/renderer/BaseMap.java @@ -28,7 +28,8 @@ import android.opengl.GLES20; import android.opengl.Matrix; /** - * This class is for rendering the Line- and PolygonLayers of visible MapTiles. For + * This class is for rendering the Line- and PolygonLayers of visible MapTiles. + * For * visible tiles that do not have data available yet its parent in children * tiles are rendered when available. * @@ -107,12 +108,12 @@ public class BaseMap { if (t.holder != null) t = t.holder; - if (t.layers == null || t.vbo == null) { + if (t.layers == null || t.layers.vbo == null) { //Log.d(TAG, "missing data " + (t.layers == null) + " " + (t.vbo == null)); return; } - GLES20.glBindBuffer(GL_ARRAY_BUFFER, t.vbo.id); + GLES20.glBindBuffer(GL_ARRAY_BUFFER, t.layers.vbo.id); // place tile relative to map position float div = FastMath.pow(tile.zoomLevel - pos.zoomLevel); @@ -135,8 +136,9 @@ public class BaseMap { int simpleShader = (pos.tilt < 1 ? 1 : 0); boolean clipped = false; + boolean lineTexture = true; - for (Layer l = t.layers.layers; l != null;) { + for (Layer l = t.layers.baseLayers; l != null;) { switch (l.type) { case Layer.POLYGON: l = PolygonRenderer.draw(pos, l, mvp, !clipped, true); @@ -144,16 +146,38 @@ public class BaseMap { break; case Layer.LINE: + //if (!lineTexture) { + LineRenderer.beginLines(); + // lineTexture = true; + //} if (!clipped) { + // draw stencil buffer clip region PolygonRenderer.draw(pos, null, mvp, true, true); clipped = true; } - l = LineRenderer.draw(pos, l, mvp, div, simpleShader, - t.layers.lineOffset); + l = LineRenderer.draw(t.layers, l, pos, mvp, div, simpleShader); + break; + + case Layer.TEXLINE: + LineRenderer.endLines(); + + if (!clipped) { + // draw stencil buffer clip region + PolygonRenderer.draw(pos, null, mvp, true, true); + clipped = true; + } + l = LineTexRenderer.draw(t.layers, l, pos, mvp, div); + lineTexture = false; + break; + + default: + // just in case + l = l.next; } } + // clear clip-region and could also draw 'fade-effect' PolygonRenderer.drawOver(mvp); } diff --git a/src/org/oscim/renderer/GLRenderer.java b/src/org/oscim/renderer/GLRenderer.java index f2c8348e..7d10e19c 100644 --- a/src/org/oscim/renderer/GLRenderer.java +++ b/src/org/oscim/renderer/GLRenderer.java @@ -74,7 +74,6 @@ public class GLRenderer implements GLSurfaceView.Renderer { // bytes currently loaded in VBOs private static int mBufferMemoryUsage; - private static float[] mTileCoords = new float[8]; private static float[] mDebugCoords = new float[8]; @@ -211,10 +210,10 @@ public class GLRenderer implements GLSurfaceView.Renderer { private static int uploadCnt = 0; - public static boolean uploadLayers(Layers layers, BufferObject vbo, int newSize, + public static boolean uploadLayers(Layers layers, int newSize, boolean addFill) { - GLES20.glBindBuffer(GL_ARRAY_BUFFER, vbo.id); + GLES20.glBindBuffer(GL_ARRAY_BUFFER, layers.vbo.id); // add fill coordinates if (addFill) @@ -241,23 +240,23 @@ public class GLRenderer implements GLSurfaceView.Renderer { if (newSize != sbuf.remaining()) { Log.d(TAG, "wrong size: " - + newSize + " " - + sbuf.position() + " " - + sbuf.limit() + " " - + sbuf.remaining()); + + " new size: " + newSize + + " buffer pos: " + sbuf.position() + + " buffer limit: " + sbuf.limit() + + " buffer fill: " + sbuf.remaining()); return false; } newSize *= SHORT_BYTES; // reuse memory allocated for vbo when possible and allocated // memory is less then four times the new data - if (vbo.size > newSize && vbo.size < newSize * 4 + if (layers.vbo.size > newSize && layers.vbo.size < newSize * 4 && mBufferMemoryUsage < LIMIT_BUFFERS) { GLES20.glBufferSubData(GL_ARRAY_BUFFER, 0, newSize, sbuf); } else { - mBufferMemoryUsage += newSize - vbo.size; - vbo.size = newSize; - GLES20.glBufferData(GL_ARRAY_BUFFER, vbo.size, sbuf, GL_DYNAMIC_DRAW); + mBufferMemoryUsage += newSize - layers.vbo.size; + layers.vbo.size = newSize; + GLES20.glBufferData(GL_ARRAY_BUFFER, layers.vbo.size, sbuf, GL_DYNAMIC_DRAW); } return true; @@ -272,15 +271,16 @@ public class GLRenderer implements GLSurfaceView.Renderer { int newSize = tile.layers.getSize(); if (newSize > 0) { - if (tile.vbo == null) - tile.vbo = BufferObject.get(newSize); + if (tile.layers.vbo == null) + tile.layers.vbo = BufferObject.get(newSize); - if (!uploadLayers(tile.layers, tile.vbo, newSize, true)) { + if (!uploadLayers(tile.layers, newSize, true)) { Log.d(TAG, "BUG uploadTileData " + tile + " failed!"); + + BufferObject.release(tile.layers.vbo); + tile.layers.vbo = null; tile.layers.clear(); tile.layers = null; - BufferObject.release(tile.vbo); - tile.vbo = null; } } } @@ -376,7 +376,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { MapPosition pos = mMapPosition; float[] coords = mTileCoords; boolean changed; - synchronized(mMapViewPosition){ + synchronized (mMapViewPosition) { changed = mMapViewPosition.getMapPosition(pos); mMapViewPosition.getMapViewProjection(coords); mMapViewPosition.getMatrix(mMatrices.view, null, mMatrices.viewproj); @@ -621,7 +621,9 @@ public class GLRenderer implements GLSurfaceView.Renderer { // String ext = GLES20.glGetString(GLES20.GL_EXTENSIONS); // Log.d(TAG, "Extensions: " + ext); + // classes that require GL context for initialization LineRenderer.init(); + LineTexRenderer.init(); PolygonRenderer.init(); TextureRenderer.init(); TextureObject.init(10); diff --git a/src/org/oscim/renderer/LineRenderer.java b/src/org/oscim/renderer/LineRenderer.java index 10c1e271..c002525a 100644 --- a/src/org/oscim/renderer/LineRenderer.java +++ b/src/org/oscim/renderer/LineRenderer.java @@ -27,6 +27,7 @@ import static android.opengl.GLES20.glVertexAttribPointer; import org.oscim.core.MapPosition; import org.oscim.generator.TileGenerator; import org.oscim.renderer.layer.Layer; +import org.oscim.renderer.layer.Layers; import org.oscim.renderer.layer.LineLayer; import org.oscim.theme.renderinstruction.Line; import org.oscim.utils.GlUtils; @@ -34,10 +35,6 @@ import org.oscim.utils.GlUtils; import android.opengl.GLES20; import android.util.Log; -/** - * @author Hannes Janetzek - */ - public final class LineRenderer { private final static String TAG = LineRenderer.class.getName(); @@ -97,6 +94,7 @@ public final class LineRenderer { } mTexID = GlUtils.loadTexture(pixel, 128, 128, GLES20.GL_ALPHA, + GLES20.GL_NEAREST, GLES20.GL_NEAREST, GLES20.GL_MIRRORED_REPEAT, GLES20.GL_MIRRORED_REPEAT); return true; @@ -110,13 +108,13 @@ public final class LineRenderer { GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); } - public static Layer draw(MapPosition pos, Layer layer, float[] matrix, float div, - int mode, int bufferOffset) { + public static Layer draw(Layers layers, Layer curLayer, MapPosition pos, + float[] matrix, float div, int mode) { int zoom = pos.zoomLevel; float scale = pos.scale; - if (layer == null) + if (curLayer == null) return null; GLState.blend(true); @@ -131,13 +129,13 @@ public final class LineRenderer { GLState.enableVertexArrays(hLineVertexPosition[mode], -1); glVertexAttribPointer(hLineVertexPosition[mode], 4, GL_SHORT, - false, 0, bufferOffset + LINE_VERTICES_DATA_POS_OFFSET); + false, 0, layers.lineOffset + LINE_VERTICES_DATA_POS_OFFSET); glUniformMatrix4fv(hLineMatrix[mode], 1, false, matrix, 0); - // line scale factor for non fixed lines: within a zoom- - // level lines would be scaled by the factor 2 via projection. - // though lines should only scale by sqrt(2). this is achieved + // Line scale factor for non fixed lines: Within a zoom- + // level lines would be scaled by the factor 2 by view-matrix. + // Though lines should only scale by sqrt(2). This is achieved // by inverting scaling of extrusion vector with: width/sqrt(s). // within one zoom-level: 1 <= s <= 2 float s = scale / div; @@ -158,7 +156,7 @@ public final class LineRenderer { // dont increase scale when max is reached boolean strokeMaxZoom = zoom > TileGenerator.STROKE_MAX_ZOOM_LEVEL; - Layer l = layer; + Layer l = curLayer; for (; l != null && l.type == Layer.LINE; l = l.next) { LineLayer ll = (LineLayer) l; Line line = ll.line; @@ -187,7 +185,7 @@ public final class LineRenderer { } else { width = ll.width / s + o.width / lineScale; - // check min size for outline + // check min-size for outline if (o.line.min > 0 && o.width * lineScale < o.line.min * 2) continue; } @@ -223,7 +221,9 @@ public final class LineRenderer { // line width increases by sqrt(2.2). width = ll.width / lineScale; - if (ll.line.min > 0 && ll.width * lineScale < ll.line.min * 2) + // min-size hack to omit outline when line becomes + // very thin + if ((ll.line.min > 0) && (ll.width * lineScale < ll.line.min * 2)) width = (ll.width - 0.2f) / lineScale; } @@ -262,7 +262,6 @@ public final class LineRenderer { + "attribute vec4 a_pos;" + "uniform float u_mode;" + "varying vec2 v_st;" - + "varying vec2 v_mode;" + "void main() {" // scale extrusion to u_width pixel // just ignore the two most insignificant bits of a_st :) @@ -271,26 +270,24 @@ public final class LineRenderer { // last two bits of a_st hold the texture coordinates // ..maybe one could wrap texture so that `abs` is not required + " v_st = abs(mod(dir, 4.0)) - 1.0;" - + " v_mode = vec2(1.0 - u_mode, u_mode);" + "}"; private final static String lineSimpleFragmentShader = "" + "precision mediump float;" + "uniform sampler2D tex;" + "uniform float u_wscale;" + + "uniform float u_mode;" + "uniform vec4 u_color;" + "varying vec2 v_st;" - + "varying vec2 v_mode;" + "void main() {" //+ " float len;" - // some say one should not use conditionals - // (FIXME currently required as overlay line renderers dont load the texture) + // (currently required as overlay line renderers dont load the texture) //+ " if (u_mode == 0)" //+ " len = abs(v_st.s);" //+ " else" //+ " len = texture2D(tex, v_st).a;" - // one trick to avoid branching, need to check performance - + " float len = max(v_mode[0] * abs(v_st.s), v_mode[1] * texture2D(tex, v_st).a);" + // this avoids branching, need to check performance + + " float len = max((1.0 - u_mode) * abs(v_st.s), u_mode * 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);" diff --git a/src/org/oscim/renderer/LineTexRenderer.java b/src/org/oscim/renderer/LineTexRenderer.java new file mode 100644 index 00000000..07217d98 --- /dev/null +++ b/src/org/oscim/renderer/LineTexRenderer.java @@ -0,0 +1,287 @@ +/* + * Copyright 2013 Hannes Janetzek + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.renderer; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; + +import org.oscim.core.MapPosition; +import org.oscim.renderer.layer.Layer; +import org.oscim.renderer.layer.Layers; +import org.oscim.renderer.layer.LineTexLayer; +import org.oscim.utils.GlUtils; + +import android.opengl.GLES20; +import android.util.Log; + +public class LineTexRenderer { + private final static String TAG = LineTexRenderer.class.getName(); + + private static int shader; + private static int hVertexPosition0; + private static int hVertexPosition1; + private static int hVertexLength0; + private static int hVertexLength1; + private static int hVertexFlip; + private static int hMatrix; + private static int hTexColor; + private static int hBgColor; + private static int hScale; + + private static int mIndicesBufferID; + private static int mVertexFlipID; + + // draw up to 100 quads in one pass + + private static int maxQuads = 100; + private static int maxIndices = maxQuads * 6; + private static int mTexID; + + public static void init() { + shader = GlUtils.createProgram(vertexShader, + fragmentShader); + if (shader == 0) { + Log.e(TAG, "Could not create program."); + return; + } + hMatrix = GLES20.glGetUniformLocation(shader, "u_mvp"); + hTexColor = GLES20.glGetUniformLocation(shader, "u_color"); + hBgColor = GLES20.glGetUniformLocation(shader, "u_bgcolor"); + hScale = GLES20.glGetUniformLocation(shader, "u_scale"); + + hVertexPosition0 = GLES20.glGetAttribLocation(shader, "a_pos0"); + hVertexPosition1 = GLES20.glGetAttribLocation(shader, "a_pos1"); + hVertexLength0 = GLES20.glGetAttribLocation(shader, "a_len0"); + hVertexLength1 = GLES20.glGetAttribLocation(shader, "a_len1"); + hVertexFlip = GLES20.glGetAttribLocation(shader, "a_flip"); + + int[] mVboIds = new int[2]; + GLES20.glGenBuffers(2, mVboIds, 0); + mIndicesBufferID = mVboIds[0]; + mVertexFlipID = mVboIds[1]; + + // 0, 1, 0, 1 + byte[] flip = new byte[maxQuads * 4]; + for (int i = 0; i < flip.length; i++) + flip[i] = (byte) (i % 2); + + short j = 0; + //mNumIndices = ((points.length) >> 2) * 6; + + short[] indices = new short[maxIndices]; + for (int i = 0; i < maxIndices; i += 6, j += 4) { + indices[i + 0] = (short) (j + 0); + indices[i + 1] = (short) (j + 1); + indices[i + 2] = (short) (j + 2); + + indices[i + 3] = (short) (j + 2); + indices[i + 4] = (short) (j + 1); + indices[i + 5] = (short) (j + 3); + } + + ByteBuffer buf = ByteBuffer.allocateDirect(maxIndices * 2) + .order(ByteOrder.nativeOrder()); + + ShortBuffer sbuf = buf.asShortBuffer(); + sbuf.put(indices); + sbuf.flip(); + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferID); + GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, indices.length * 2, sbuf, + GLES20.GL_STATIC_DRAW); + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); + + buf.clear(); + buf.put(flip); + buf.flip(); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexFlipID); + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, flip.length, buf, + GLES20.GL_STATIC_DRAW); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + byte[] stipple = new byte[2]; + stipple[0] = 8; + stipple[1] = 8; + //stipple[2] = 16; + //stipple[3] = 48; + + mTexID = GlUtils.loadStippleTexture(stipple); + + } + + private final static int STRIDE = 12; + private final static int LEN_OFFSET = 8; + + public static Layer draw(Layers layers, Layer curLayer, + MapPosition pos, float[] matrix, float div) { + + GLState.blend(true); + GLState.useProgram(shader); + + GLState.enableVertexArrays(-1, -1); + + GLES20.glEnableVertexAttribArray(hVertexPosition0); + GLES20.glEnableVertexAttribArray(hVertexPosition1); + GLES20.glEnableVertexAttribArray(hVertexLength0); + GLES20.glEnableVertexAttribArray(hVertexLength1); + GLES20.glEnableVertexAttribArray(hVertexFlip); + + GLES20.glUniformMatrix4fv(hMatrix, 1, false, matrix, 0); + GLES20.glUniform1f(hScale, pos.scale / div); + + GLES20.glUniform4f(hTexColor, 1.0f, 1.0f, 1.0f, 1.0f); + //aa9988 + + GLES20.glUniform4f(hBgColor, 0x99 / 255f, 0x96 / 255f, 0x93 / 255f, 0.95f); + + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexID); + + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, + mIndicesBufferID); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexFlipID); + GLES20.glVertexAttribPointer(hVertexFlip, 1, + GLES20.GL_BYTE, false, 0, 0); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, layers.vbo.id); + + int offset = layers.texLineOffset; + + Layer l = curLayer; + while (l != null && l.type == Layer.TEXLINE) { + LineTexLayer ll = (LineTexLayer) l; + //Line line = ll.line; + + // first pass + int allIndices = (ll.evenQuads * 6); + for (int i = 0; i < allIndices; i += maxIndices) { + int numIndices = allIndices - i; + if (numIndices > maxIndices) + numIndices = maxIndices; + + // i / 6 * (24 shorts per block * 2 short bytes) + int add = offset + i * 8; + + GLES20.glVertexAttribPointer(hVertexPosition0, + 4, GLES20.GL_SHORT, false, STRIDE, + add + STRIDE); + + GLES20.glVertexAttribPointer(hVertexLength0, + 2, GLES20.GL_SHORT, false, STRIDE, + add + STRIDE + LEN_OFFSET); + + GLES20.glVertexAttribPointer(hVertexPosition1, + 4, GLES20.GL_SHORT, false, STRIDE, + add); + + GLES20.glVertexAttribPointer(hVertexLength1, + 2, GLES20.GL_SHORT, false, STRIDE, + add + LEN_OFFSET); + + GLES20.glDrawElements(GLES20.GL_TRIANGLES, numIndices, + GLES20.GL_UNSIGNED_SHORT, 0); + } + + // second pass + allIndices = (ll.oddQuads * 6); + for (int i = 0; i < allIndices; i += maxIndices) { + int numIndices = allIndices - i; + if (numIndices > maxIndices) + numIndices = maxIndices; + // i / 6 * (24 shorts per block * 2 short bytes) + int add = offset + i * 8; + + GLES20.glVertexAttribPointer(hVertexPosition0, + 4, GLES20.GL_SHORT, false, STRIDE, + add + 2 * STRIDE); + + GLES20.glVertexAttribPointer(hVertexLength0, + 2, GLES20.GL_SHORT, false, STRIDE, + add + 2 * STRIDE + LEN_OFFSET); + + GLES20.glVertexAttribPointer(hVertexPosition1, + 4, GLES20.GL_SHORT, false, STRIDE, + add + STRIDE); + + GLES20.glVertexAttribPointer(hVertexLength1, + 2, GLES20.GL_SHORT, false, STRIDE, + add + STRIDE + LEN_OFFSET); + + GLES20.glDrawElements(GLES20.GL_TRIANGLES, numIndices, + GLES20.GL_UNSIGNED_SHORT, 0); + } + + l = l.next; + } + + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); + + GLES20.glDisableVertexAttribArray(hVertexPosition0); + GLES20.glDisableVertexAttribArray(hVertexPosition1); + GLES20.glDisableVertexAttribArray(hVertexLength0); + GLES20.glDisableVertexAttribArray(hVertexLength1); + GLES20.glDisableVertexAttribArray(hVertexFlip); + GlUtils.checkGlError("..."); + + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + + return l; + } + + final static String vertexShader = "" + + "precision mediump float;" + + "uniform mat4 u_mvp;" + + "uniform vec4 u_color;" + + "uniform float u_scale;" + + "attribute vec4 a_pos0;" + + "attribute vec4 a_pos1;" + + "attribute vec2 a_len0;" + + "attribute vec2 a_len1;" + + "attribute float a_flip;" + + "varying vec2 v_st;" + + "void main() {" + + " float div = (8.0 * 16.0) / max(ceil(log(u_scale)),1.0);" + + " if (a_flip == 0.0){" + + " vec2 dir = a_pos0.zw/16.0;" + + " gl_Position = u_mvp * vec4(a_pos0.xy + dir / u_scale, 0.0, 1.0);" + + " v_st = vec2(a_len0.x/div, 1.0);" + + " }else {" + + " vec2 dir = a_pos1.zw/16.0;" + + " gl_Position = u_mvp * vec4(a_pos1.xy - dir / u_scale, 0.0, 1.0);" + + " v_st = vec2(a_len1.x/div, -1.0);" + + " }" + + "}"; + + final static String fragmentShader = "" + + "#extension GL_OES_standard_derivatives : enable\n" + + "precision mediump float;" + + "uniform sampler2D tex;" + + " uniform vec4 u_color;" + + " uniform vec4 u_bgcolor;" + + "varying vec2 v_st;" + + "void main() {" + + " float len = texture2D(tex, v_st).a;" + + " float tex_w = abs(v_st.t);" + + " vec2 st_width = fwidth(v_st);" + + " float fuzz = max(st_width.s, st_width.t);" + + " float line_w = (1.0 - smoothstep(1.0 - fuzz, 1.0, tex_w));" + + " float stipple_w = (1.0 - smoothstep(0.7 - fuzz, 0.7, tex_w));" + + " float stipple_p = smoothstep(0.495, 0.505, len);" + + " gl_FragColor = line_w * mix(u_bgcolor, u_color, min(stipple_w, stipple_p));" + + //+ " gl_FragColor = u_color * min(abs(1.0 - mod(v_len, 20.0)/10.0), (1.0 - abs(v_st.x)));" + + "}"; + +} diff --git a/src/org/oscim/renderer/MapTile.java b/src/org/oscim/renderer/MapTile.java index bbb52f3d..49ee4e4f 100644 --- a/src/org/oscim/renderer/MapTile.java +++ b/src/org/oscim/renderer/MapTile.java @@ -37,15 +37,6 @@ public final class MapTile extends JobTile { */ public boolean isVisible; - /** - * VBO holds all vertex data to draw lines and polygons when - * 'layers' are compiled. layout: - * 16 bytes fill coordinates, - * n bytes polygon vertices, - * m bytes lines vertices - */ - BufferObject vbo; - /** * Pointer to access relatives in QuadTree */ diff --git a/src/org/oscim/renderer/TileManager.java b/src/org/oscim/renderer/TileManager.java index 8e929b46..c8477c75 100644 --- a/src/org/oscim/renderer/TileManager.java +++ b/src/org/oscim/renderer/TileManager.java @@ -162,7 +162,7 @@ public class TileManager { MapPosition mapPosition = mMapPosition; float[] coords = mTileCoords; - synchronized(mMapViewPosition){ + synchronized (mMapViewPosition) { changedPos = mMapViewPosition.getMapPosition(mapPosition); mMapViewPosition.getMapViewProjection(coords); } @@ -390,17 +390,18 @@ public class TileManager { return; if (t.layers != null) { + // TODO move this to layers clear + if (t.layers.vbo != null) { + BufferObject.release(t.layers.vbo); + t.layers.vbo = null; + } + t.layers.clear(); t.layers = null; } TextItem.release(t.labels); - if (t.vbo != null) { - BufferObject.release(t.vbo); - t.vbo = null; - } - QuadTree.remove(t); t.state = STATE_NONE; @@ -522,19 +523,19 @@ public class TileManager { tiles[i] = null; } } -// if (locked) { -// Log.d(TAG, "------------ " -// + remove + " / " + r + " " -// + mMapPosition.zoomLevel -// + " ----------"); -// for (int i = 0; i < size; i++) { -// MapTile t = tiles[i]; -// if (t == null) -// continue; -// Log.d(TAG, "limitCache: " + t + " " + t.distance); -// -// } -// } + //if (locked) { + // Log.d(TAG, "------------ " + // + remove + " / " + r + " " + // + mMapPosition.zoomLevel + // + " ----------"); + // for (int i = 0; i < size; i++) { + // MapTile t = tiles[i]; + // if (t == null) + // continue; + // Log.d(TAG, "limitCache: " + t + " " + t.distance); + // + // } + //} remove = (newTileCnt - MAX_TILES_IN_QUEUE) + 10; //int r = remove; for (int i = size - 1; i >= 0 && remove > 0; i--) { @@ -572,11 +573,11 @@ public class TileManager { return true; } - if (tile.vbo != null) { - // BAD Things(tm) happend: tile is already loaded - Log.d(TAG, "BUG: tile loaded before " + tile); - return true; - } + //if (tile.vbo != null) { + // // BAD Things(tm) happend: tile is already loaded + // Log.d(TAG, "BUG: tile loaded before " + tile); + // return true; + //} tile.state = STATE_NEW_DATA; mTilesForUpload++; diff --git a/src/org/oscim/renderer/layer/ExtrusionLayer.java b/src/org/oscim/renderer/layer/ExtrusionLayer.java index fd64c0c7..bb95f2f9 100644 --- a/src/org/oscim/renderer/layer/ExtrusionLayer.java +++ b/src/org/oscim/renderer/layer/ExtrusionLayer.java @@ -64,7 +64,7 @@ public class ExtrusionLayer extends Layer { public ExtrusionLayer(int level) { this.type = Layer.EXTRUSION; - this.layer = level; + this.level = level; mVertices = mCurVertices = VertexPool.get(); @@ -346,6 +346,7 @@ public class ExtrusionLayer extends Layer { return convex; } + @Override public void compile(ShortBuffer sbuf) { if (mNumVertices == 0 || compiled) diff --git a/src/org/oscim/renderer/layer/Layer.java b/src/org/oscim/renderer/layer/Layer.java index 1562bbb5..04e6c582 100644 --- a/src/org/oscim/renderer/layer/Layer.java +++ b/src/org/oscim/renderer/layer/Layer.java @@ -14,24 +14,28 @@ */ package org.oscim.renderer.layer; +import java.nio.ShortBuffer; + /** * @authorHannes Janetzek */ public abstract class Layer { public final static byte LINE = 0; public final static byte POLYGON = 1; - public final static byte WAYTEXT = 2; - public final static byte POITEXT = 3; - public final static byte SYMBOL = 4; - public final static byte BITMAP = 5; - public final static byte TEXLINE = 6; + public final static byte TEXLINE = 2; + public final static byte WAYTEXT = 3; + public final static byte POITEXT = 4; + public final static byte SYMBOL = 5; + public final static byte BITMAP = 6; public final static byte EXTRUSION = 7; public byte type = -1; public Layer next; - int layer; + // drawing order from bottom to top + int level; + // number of vertices for this layer public int verticesCnt; @@ -44,5 +48,6 @@ public abstract class Layer { VertexPoolItem pool; protected VertexPoolItem curItem; + abstract protected void compile(ShortBuffer sbuf); abstract protected void clear(); } diff --git a/src/org/oscim/renderer/layer/Layers.java b/src/org/oscim/renderer/layer/Layers.java index f6124226..4d2c1010 100644 --- a/src/org/oscim/renderer/layer/Layers.java +++ b/src/org/oscim/renderer/layer/Layers.java @@ -16,22 +16,33 @@ package org.oscim.renderer.layer; import java.nio.ShortBuffer; +import org.oscim.renderer.BufferObject; + import android.util.Log; -/** - * @author Hannes Janetzek - */ public class Layers { - // mixed Polygon and Line layers - public Layer layers; + private final static String TAG = Layers.class.getName(); + + // mixed Polygon- and LineLayer + public Layer baseLayers; public Layer textureLayers; public Layer extrusionLayers; + // VBO holds all vertex data to draw lines and polygons + // after are compilation. + // Layout: + // 16 bytes fill coordinates, + // n bytes polygon vertices, + // m bytes lines vertices + // ... + public BufferObject vbo; + // To not need to switch VertexAttribPointer positions all the time: // 1. polygons are packed in VBO at offset 0 // 2. lines afterwards at lineOffset // 3. other layers keep their byte offset in Layer.offset public int lineOffset; + public int texLineOffset; // time when layers became first rendered (in uptime) // used for animations @@ -39,93 +50,109 @@ public class Layers { private Layer mCurLayer; - // get or add the line- or polygon-layer for a level. + // get or add the Line- or PolygonLayer for a level. public Layer getLayer(int level, byte type) { - Layer l = layers; - Layer ret = null; + Layer l = baseLayers; + Layer layer = null; - if (mCurLayer != null && mCurLayer.layer == level) { - ret = mCurLayer; - } else if (l == null || l.layer > level) { - // insert new layer at start - l = null; + if (mCurLayer != null && mCurLayer.level == level) { + layer = mCurLayer; } else { - while (true) { - if (l.layer == level) { - // found layer - ret = l; - break; - } - if (l.next == null || l.next.layer > level) { + if (l == null || l.level > level) { + // insert new layer at start + l = null; + } else { + while (true) { + // found layer + if (l.level == level) { + layer = l; + break; + } + // insert new layer between current and next layer - break; + if (l.next == null || l.next.level > level) + break; + + l = l.next; + } + } + + if (layer == null) { + // add a new Layer + if (type == Layer.LINE) + layer = new LineLayer(level); + else if (type == Layer.POLYGON) + layer = new PolygonLayer(level); + else if (type == Layer.TEXLINE) + layer = new LineTexLayer(level); + else + // TODO throw execption + return null; + + if (l == null) { + // insert at start + layer.next = baseLayers; + baseLayers = layer; + } else { + layer.next = l.next; + l.next = layer; } - l = l.next; } } - if (ret == null) { - if (type == Layer.LINE) - ret = new LineLayer(level); - else if (type == Layer.POLYGON) - ret = new PolygonLayer(level); - else - return null; - if (l == null) { - // insert at start - ret.next = layers; - layers = ret; - } else { - ret.next = l.next; - l.next = ret; - } - } else if (ret.type != type) { - Log.d("...", "wrong layer type " + ret.type + " " + type); - // FIXME thorw exception + if (layer.type != type) { + // check if found layer matches requested type + Log.d(TAG, "BUG wrong layer " + layer.type + " " + type); + // TODO throw exception return null; } - return ret; + mCurLayer = layer; + + return layer; } - private static int LINE_VERTEX_SHORTS = 4; - private static int POLY_VERTEX_SHORTS = 2; - private static int TEXTURE_VERTEX_SHORTS = 6; + private final static int[] VERTEX_SHORT_CNT = { + 4, // LINE_VERTEX_SHORTS + 2, // POLY_VERTEX_SHORTS + 6, // TEXLINE_VERTEX_SHORTS + }; - //private static int EXTRUSION_VERTEX_SHORTS = 4; + private final static int TEXTURE_VERTEX_SHORTS = 6; + + private final static int SHORT_BYTES = 2; public int getSize() { - int size = 0; - for (Layer l = layers; l != null; l = l.next) { - if (l.type == Layer.LINE) - size += l.verticesCnt * LINE_VERTEX_SHORTS; - else - size += l.verticesCnt * POLY_VERTEX_SHORTS; - } + for (Layer l = baseLayers; l != null; l = l.next) + size += l.verticesCnt * VERTEX_SHORT_CNT[l.type]; for (Layer l = textureLayers; l != null; l = l.next) size += l.verticesCnt * TEXTURE_VERTEX_SHORTS; - //for (Layer l = extrusionLayers; l != null; l = l.next) - // size += l.verticesCnt * EXTRUSION_VERTEX_SHORTS; - return size; } public void compile(ShortBuffer sbuf, boolean addFill) { // offset from fill coordinates int pos = 0; - if (addFill) + int size = 0; + + if (addFill){ pos = 4; + size = 8; + } - // add polygons first, needed to get the offsets right... - addLayerItems(sbuf, layers, Layer.POLYGON, pos); + size += addLayerItems(sbuf, baseLayers, Layer.POLYGON, pos); - lineOffset = sbuf.position() * 2; // * short-bytes - addLayerItems(sbuf, layers, Layer.LINE, 0); + lineOffset = size * SHORT_BYTES; + size += addLayerItems(sbuf, baseLayers, Layer.LINE, 0); + + + texLineOffset = size * SHORT_BYTES; + size += addLayerItems(sbuf, baseLayers, Layer.TEXLINE, 0); for (Layer l = textureLayers; l != null; l = l.next) { TextureLayer tl = (TextureLayer) l; @@ -139,20 +166,33 @@ public class Layers { // } } - // optimization for lines and polygon: collect all pool items and add back in one go - private static void addLayerItems(ShortBuffer sbuf, Layer l, byte type, int pos) { + // optimization for Line- and PolygonLayer: + // collect all pool items and add back in one go + private static int addLayerItems(ShortBuffer sbuf, Layer l, byte type, int pos) { VertexPoolItem last = null, items = null; + int size = 0; + + // HACK, see LineTexLayer + boolean addOffset = (type == Layer.TEXLINE); for (; l != null; l = l.next) { if (l.type != type) continue; - for (VertexPoolItem it = l.pool; it != null; it = it.next) { - if (it.next == null) - sbuf.put(it.vertices, 0, it.used); - else - sbuf.put(it.vertices, 0, VertexPoolItem.SIZE); + if (addOffset){ + sbuf.position(sbuf.position() + 6); + addOffset = false; + } + for (VertexPoolItem it = l.pool; it != null; it = it.next) { + if (it.next == null){ + size += it.used; + sbuf.put(it.vertices, 0, it.used); + } + else{ + size += VertexPoolItem.SIZE; + sbuf.put(it.vertices, 0, VertexPoolItem.SIZE); + } last = it; } if (last == null) @@ -169,11 +209,13 @@ public class Layers { l.curItem = null; } VertexPool.release(items); + + return size; } static void addPoolItems(Layer l, ShortBuffer sbuf) { // offset of layer data in vbo - l.offset = sbuf.position() * 2; // (* short-bytes) + l.offset = sbuf.position() * SHORT_BYTES; for (VertexPoolItem it = l.pool; it != null; it = it.next) { if (it.next == null) @@ -190,7 +232,7 @@ public class Layers { public void clear() { // clear line and polygon layers directly - Layer l = layers; + Layer l = baseLayers; while (l != null) { if (l.pool != null) { VertexPool.release(l.pool); @@ -207,8 +249,13 @@ public class Layers { for (l = extrusionLayers; l != null; l = l.next) { l.clear(); } - layers = null; + baseLayers = null; textureLayers = null; extrusionLayers = null; + + // if (vbo != null){ + // BufferObject.release(vbo); + // vbo = null; + // } } } diff --git a/src/org/oscim/renderer/layer/LineLayer.java b/src/org/oscim/renderer/layer/LineLayer.java index bb82365c..d1443e3b 100644 --- a/src/org/oscim/renderer/layer/LineLayer.java +++ b/src/org/oscim/renderer/layer/LineLayer.java @@ -14,6 +14,8 @@ */ package org.oscim.renderer.layer; +import java.nio.ShortBuffer; + import org.oscim.core.Tile; import org.oscim.renderer.GLRenderer; import org.oscim.theme.renderinstruction.Line; @@ -26,7 +28,6 @@ import android.graphics.Paint.Cap; * @author Hannes Janetzek */ public final class LineLayer extends Layer { - private static final float COORD_SCALE = GLRenderer.COORD_MULTIPLIER; // scale factor mapping extrusion vector to short values public static final float DIR_SCALE = 2048; @@ -42,7 +43,7 @@ public final class LineLayer extends Layer { public boolean roundCap; LineLayer(int layer) { - this.layer = layer; + this.level = layer; this.type = Layer.LINE; } @@ -312,11 +313,13 @@ public final class LineLayer extends Layer { vx *= -1; vy *= -1; + int end = pos + length; + for (;;) { - if (ipos < pos + length) { + if (ipos < end) { nextX = points[ipos++]; nextY = points[ipos++]; - } else if (closed && ipos < pos + length + 2) { + } else if (closed && ipos < end + 2) { // add startpoint == endpoint nextX = points[pos]; nextY = points[pos + 1]; @@ -543,4 +546,8 @@ public final class LineLayer extends Layer { @Override protected void clear() { } + + @Override + protected void compile(ShortBuffer sbuf) { + } } diff --git a/src/org/oscim/renderer/layer/LineTexLayer.java b/src/org/oscim/renderer/layer/LineTexLayer.java new file mode 100644 index 00000000..443089fb --- /dev/null +++ b/src/org/oscim/renderer/layer/LineTexLayer.java @@ -0,0 +1,229 @@ +/* + * Copyright 2013 Hannes Janetzek + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.renderer.layer; + +import java.nio.ShortBuffer; + +import org.oscim.renderer.GLRenderer; +import org.oscim.theme.renderinstruction.Line; +import org.oscim.utils.FastMath; + +/** + * Layer for textured or stippled lines + */ +public final class LineTexLayer extends Layer { + // Interleave two segment quads in one block to be able to use + // vertices twice. pos0 and pos1 use the same vertex array where + // pos1 has an offset of one vertex. The vertex shader will use + // pos0 when the vertexId is even, pos1 when the Id is odd. + // + // As there is no gl_VertexId in gles 2.0 an additional 'flip' + // array is used. Depending on 'flip' extrusion is inverted. + // + // Indices and flip buffers can be static. + // + // First pass: using even vertex array positions + // (used vertices are in braces) + // vertex id 0 1 2 3 4 5 6 7 + // pos0 x (0) 1 (2) 3 (4) 5 (6) 7 x + // pos1 x (0) 1 (2) 3 (4) 5 (6) 7 x + // flip 0 1 0 1 0 1 0 1 + // + // Second pass: using odd vertex array positions + // vertex id 0 1 2 3 4 5 6 7 + // pos0 x 0 (1) 2 (3) 4 (5) 6 (7) x + // pos1 x 0 (1) 2 (3) 4 (5) 6 (7) x + // flip 0 1 0 1 0 1 0 1 + // + // Vertex layout: + // [2 short] position, + // [2 short] extrusion, + // [1 short] line length + // [1 short] unused + // + // indices, for two blocks: + // 0, 1, 2, + // 2, 1, 3, + // 4, 5, 6, + // 6, 5, 7, + + private static final float COORD_SCALE = GLRenderer.COORD_MULTIPLIER; + // scale factor mapping extrusion vector to short values + public static final float DIR_SCALE = 255; //2048; + + // lines referenced by this outline layer + public LineLayer outlines; + public Line line; + public float width; + + public boolean roundCap; + + public int evenQuads; + public int oddQuads; + + private boolean evenSegment; + + LineTexLayer(int layer) { + this.level = layer; + this.type = Layer.TEXLINE; + this.evenSegment = true; + } + + public void addLine(float[] points, short[] index) { + + if (pool == null) { + curItem = pool = VertexPool.get(); + // need to make sure there is one unused + // vertex in front for interleaving. + + // HACK add this offset when compiling + // otherwise one cant use the full + // VertexItem + //curItem.used = 6; + + verticesCnt = 1; + } + + VertexPoolItem si = curItem; + + short v[] = si.vertices; + int opos = si.used; + + boolean even = evenSegment; + + // reset offset to last written position + if (!even) + opos -= 12; + + int n; + int length = 0; + + if (index == null) { + n = 1; + length = points.length; + } else { + n = index.length; + } + + for (int i = 0, pos = 0; i < n; i++) { + if (index != null) + length = index[i]; + + // check end-marker in indices + if (length < 0) + break; + + // need at least two points + if (length < 4) { + pos += length; + continue; + } + + int ipos = pos; + + float x = points[ipos++] * COORD_SCALE; + float y = points[ipos++] * COORD_SCALE; + + // randomize a bit + float lineLength = FastMath.abs(x * y) % 20; + + int end = pos + length; + + for (; ipos < end;) { + float nx = points[ipos++] * COORD_SCALE; + float ny = points[ipos++] * COORD_SCALE; + + // Calculate triangle corners for the given width + float vx = nx - x; + float vy = ny - y; + + float a = (float) Math.sqrt(vx * vx + vy * vy); + + // normal vector + vx /= a; + vy /= a; + + // perpendicular to line segment + float ux = -vy; + float uy = vx; + + short dx = (short) (ux * DIR_SCALE); + short dy = (short) (uy * DIR_SCALE); + + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); + v = si.vertices; + opos = 0; + } + + v[opos + 0] = (short) x; + v[opos + 1] = (short) y; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = (short) lineLength; + v[opos + 5] = 0; + + lineLength += a; + v[opos + 12] = (short) nx; + v[opos + 13] = (short) ny; + v[opos + 14] = dx; + v[opos + 15] = dy; + v[opos + 16] = (short) lineLength; + v[opos + 17] = 0; + + x = nx; + y = ny; + + if (even) { + // go to second segment + opos += 6; + even = false; + + // vertex 0 and 2 were added + verticesCnt += 3; + evenQuads++; + } else { + // go to next block + even = true; + opos += 18; + + // vertex 1 and 3 were added + verticesCnt += 1; + oddQuads++; + } + } + + pos += length; + } + + // advance offset to last written position + if (!even) + opos += 12; + + si.used = opos; + curItem = si; + + evenSegment = even; + } + + @Override + protected void clear() { + } + + @Override + protected void compile(ShortBuffer sbuf) { + } + +} diff --git a/src/org/oscim/renderer/layer/PolygonLayer.java b/src/org/oscim/renderer/layer/PolygonLayer.java index f371f527..77a0b0cc 100644 --- a/src/org/oscim/renderer/layer/PolygonLayer.java +++ b/src/org/oscim/renderer/layer/PolygonLayer.java @@ -14,6 +14,8 @@ */ package org.oscim.renderer.layer; +import java.nio.ShortBuffer; + import org.oscim.core.Tile; import org.oscim.renderer.GLRenderer; import org.oscim.theme.renderinstruction.Area; @@ -24,7 +26,7 @@ public final class PolygonLayer extends Layer { public Area area; PolygonLayer(int layer) { - this.layer = layer; + this.level = layer; this.type = Layer.POLYGON; curItem = VertexPool.get(); pool = curItem; @@ -87,6 +89,10 @@ public final class PolygonLayer extends Layer { curItem = si; } + @Override + protected void compile(ShortBuffer sbuf) { + } + @Override protected void clear() { } diff --git a/src/org/oscim/renderer/layer/TextureLayer.java b/src/org/oscim/renderer/layer/TextureLayer.java index a5fc3aa2..3d4879dd 100644 --- a/src/org/oscim/renderer/layer/TextureLayer.java +++ b/src/org/oscim/renderer/layer/TextureLayer.java @@ -32,7 +32,8 @@ public abstract class TextureLayer extends Layer { * @param sbuf * buffer to add vertices */ - void compile(ShortBuffer sbuf) { + @Override + protected void compile(ShortBuffer sbuf) { for (TextureObject to = textures; to != null; to = to.next) TextureObject.uploadTexture(to); diff --git a/src/org/oscim/renderer/layer/VertexPoolItem.java b/src/org/oscim/renderer/layer/VertexPoolItem.java index ca3e23d9..570e96aa 100644 --- a/src/org/oscim/renderer/layer/VertexPoolItem.java +++ b/src/org/oscim/renderer/layer/VertexPoolItem.java @@ -22,7 +22,7 @@ public class VertexPoolItem { // must be multiple of // 4 (LineLayer/PolygonLayer), - // 6 (TexLineLayer) + // 24 (TexLineLayer - one block, i.e. two segments) // 24 (TextureLayer) public static final int SIZE = 360; } diff --git a/src/org/oscim/renderer/overlays/BasicOverlay.java b/src/org/oscim/renderer/overlays/BasicOverlay.java index bd328ac6..28fa5f82 100644 --- a/src/org/oscim/renderer/overlays/BasicOverlay.java +++ b/src/org/oscim/renderer/overlays/BasicOverlay.java @@ -20,6 +20,7 @@ import org.oscim.renderer.GLRenderer; import org.oscim.renderer.GLRenderer.Matrices; import org.oscim.renderer.GLState; import org.oscim.renderer.LineRenderer; +import org.oscim.renderer.LineTexRenderer; import org.oscim.renderer.PolygonRenderer; import org.oscim.renderer.TextureRenderer; import org.oscim.renderer.layer.Layer; @@ -34,8 +35,6 @@ public abstract class BasicOverlay extends RenderOverlay { public final Layers layers; - public BufferObject vbo; - protected float[] mvp = new float[16]; public BasicOverlay(MapView mapView) { @@ -51,17 +50,23 @@ public abstract class BasicOverlay extends RenderOverlay { float div = FastMath.pow(mMapPosition.zoomLevel - pos.zoomLevel); - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo.id); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, layers.vbo.id); GLState.test(false, false); - if (layers.layers != null) { + if (layers.baseLayers != null) { setMatrix(pos, m, true); - for (Layer l = layers.layers; l != null;) { - if (l.type == Layer.POLYGON) { - l = PolygonRenderer.draw(pos, l, m.mvp, true, false); - } else { - l = LineRenderer.draw(pos, l, m.mvp, div, 0, layers.lineOffset); + for (Layer l = layers.baseLayers; l != null;) { + switch (l.type) { + case Layer.POLYGON: + l = PolygonRenderer.draw(pos, l, m.mvp, true, false); + break; + case Layer.LINE: + l = LineRenderer.draw(layers, l, pos, m.mvp, div, 0); + break; + case Layer.TEXLINE: + l = LineTexRenderer.draw(layers, l, pos, m.mvp, div); + break; } } } @@ -81,21 +86,21 @@ public abstract class BasicOverlay extends RenderOverlay { public void compile() { int newSize = layers.getSize(); if (newSize == 0) { - BufferObject.release(vbo); - vbo = null; + BufferObject.release(layers.vbo); + layers.vbo = null; isReady = false; return; } - if (vbo == null) { - vbo = BufferObject.get(0); + if (layers.vbo == null) { + layers.vbo = BufferObject.get(0); - if (vbo == null) + if (layers.vbo == null) return; } if (newSize > 0) { - if (GLRenderer.uploadLayers(layers, vbo, newSize, true)) + if (GLRenderer.uploadLayers(layers, newSize, true)) isReady = true; } } diff --git a/src/org/oscim/renderer/overlays/TestLineOverlay.java b/src/org/oscim/renderer/overlays/TestLineOverlay.java index 3041bfe0..aeddbf4c 100644 --- a/src/org/oscim/renderer/overlays/TestLineOverlay.java +++ b/src/org/oscim/renderer/overlays/TestLineOverlay.java @@ -40,85 +40,51 @@ public class TestLineOverlay extends RenderOverlay { super(mapView); } - // Interleave two quads to be able to use vertices - // twice. pos0 and pos1 use the same vertex array - // where pos1 is off-setted by one vertex. The - // vertex shader will use pos0 when the vertexId - // is even, pos1 when the Id is odd. + // Interleave two segment quads in one block to be able to use + // vertices twice. pos0 and pos1 use the same vertex array where + // pos1 is off-setted by one vertex. The vertex shader will use + // pos0 when the vertexId is even, pos1 when the Id is odd. // - // As there is no gl_VertexId in gles 2.0 an - // additional 'flip' array is used. - // Depending on 'flip' extrusion is inverted. + // As there is no gl_VertexId in gles 2.0 an additional 'flip' + // array is used. Depending on 'flip' extrusion is inverted. // // Indices and flip buffers can be static. // // First pass: using even vertex array positions // (used vertices are in braces) - // vertex id 0, 1, 2, 3 - // pos0 - (0) 1 (2) 3 - - // pos1 - (0) 1 (2) 3 - - // flip 0 1 0 1 + // vertex id 0 1 2 3 4 5 6 7 + // pos0 - (0) 1 (2) 3 (4) 5 (6) 7 - + // pos1 - (0) 1 (2) 3 (4) 5 (6) 7 - + // flip 0 1 0 1 0 1 0 1 // // Second pass: using odd vertex array positions - // vertex id 0, 1, 2, 3 - // pos0 - 0 (1) 2 (3) - - // pos1 - 0 (1) 2 (3) - - // flip 0 1 0 1 + // vertex id 0 1 2 3 4 5 6 7 + // pos0 - 0 (1) 2 (3) 4 (5) 6 (7) - + // pos1 - 0 (1) 2 (3) 4 (5) 6 (7) - + // flip 0 1 0 1 0 1 0 1 // // Vertex layout: - // x/y pos[16][16], dir_x[8]|dir_y[8], start[4]|length[12] - // - 'direction' precision 1/16, maximum line width is 2*16 - // - texture 'start' prescision 1 - // -> max tex width is 32 - // - segment 'length' prescision 1/4 - // -> max line length is 2^12/4=1024 - // - texture 'end' is 'length'-'start' - - // private final short[] box = { - // // '-' start - // 0, 0, 0, 0, - // // 0. - // -800, 0, 255, 0, - // // 2. - // 100, 0, 255, 0, - // // 1. - // 0, 0, 255, 1, - // // 3. - // 800, 0, 255, 1, + // [2 short] position, + // [2 short] extrusion, + // [1 short] line length + // [1 short] unused // - // -800, 200, 127, 0, - // 0, 200, 127, 0, - // 0, 200, 127, 1, - // 800, 200, 127, 1, - // - // -800, 400, 255, 0, - // 0, 400, 255, 0, - // 0, 400, 255, 1, - // 800, 400, 255, 1, - // - // // '-' end - // 0, 0, 0, 0, - // }; - - private short[] indices = { - 0, 1, 2, - 2, 1, 3, - - 4, 5, 6, - 6, 5, 7, - - 8, 9, 10, - 10, 9, 11, - }; - - private byte[] flip; + // indices: (two indice blocks) + // 0, 1, 2, + // 2, 1, 3, + // 4, 5, 6, + // 6, 5, 7, private static int testProgram; private static int htestVertexPosition0; private static int htestVertexPosition1; + private static int htestVertexLength0; + private static int htestVertexLength1; private static int htestVertexFlip; private static int htestMatrix; - private static int htestColor; + private static int htestTexColor; + private static int htestBgColor; + private static int htestScale; private boolean initialized = false; @@ -126,38 +92,52 @@ public class TestLineOverlay extends RenderOverlay { + "precision mediump float;" + "uniform mat4 u_mvp;" + "uniform vec4 u_color;" + + "uniform float u_scale;" + "attribute vec4 a_pos0;" + "attribute vec4 a_pos1;" + + "attribute vec2 a_len0;" + + "attribute vec2 a_len1;" + "attribute float a_flip;" - + "varying vec4 color;" - + "const float ff = 256.0;" - + "const float ffff = 65536.0;" + + "varying vec2 v_st;" + "void main() {" + + " float div = (8.0 * 16.0) / max(ceil(log(u_scale)),1.0);" + " if (a_flip == 0.0){" - // extract 8 bit direction vector + " vec2 dir = a_pos0.zw/16.0;" - + " gl_Position = u_mvp * vec4(a_pos0.xy + dir, 0.0, 1.0);" - + " color = vec4(dir/255.0 + 0.5, 1.0,1.0);" + + " gl_Position = u_mvp * vec4(a_pos0.xy + dir / u_scale, 0.0, 1.0);" + + " v_st = vec2(a_len0.x/div, 1.0);" + " }else {" + " vec2 dir = a_pos1.zw/16.0;" - + " gl_Position = u_mvp * vec4(a_pos1.xy - dir, 0.0, 1.0);" - + " color = vec4(dir/255.0 + 0.5, 1.0,1.0);" - + "}}"; + + " gl_Position = u_mvp * vec4(a_pos1.xy - dir / u_scale, 0.0, 1.0);" + + " v_st = vec2(a_len1.x/div, -1.0);" + + " }" + + "}"; final static String testFragmentShader = "" + "precision mediump float;" - + "varying vec4 color;" + + "uniform sampler2D tex;" + + " uniform vec4 u_color;" + + " uniform vec4 u_bgcolor;" + + "varying vec2 v_st;" + "void main() {" - + " gl_FragColor = color;" + + " float len = texture2D(tex, v_st).a;" + + " float tex_w = abs(v_st.t);" + + " float line_w = (1.0 - smoothstep(0.7, 1.0, tex_w));" + + " float stipple_w = (1.0 - smoothstep(0.1, 0.6, tex_w));" + + " float stipple_p = smoothstep(0.495, 0.505, len);" + + " gl_FragColor = line_w * mix(u_bgcolor, u_color, min(stipple_w, stipple_p));" + + //+ " gl_FragColor = u_color * min(abs(1.0 - mod(v_len, 20.0)/10.0), (1.0 - abs(v_st.x)));" + "}"; private int mIndicesBufferID; private int mVertexBufferID; private int mVertexFlipID; - private int mNumVertices; + //private int mNumVertices; private int mNumIndices; + private int mTexID; + @Override public synchronized void update(MapPosition curPos, boolean positionChanged, boolean tilesChanged) { @@ -174,9 +154,14 @@ public class TestLineOverlay extends RenderOverlay { return; } htestMatrix = GLES20.glGetUniformLocation(testProgram, "u_mvp"); - htestColor = GLES20.glGetUniformLocation(testProgram, "u_color"); + htestTexColor = GLES20.glGetUniformLocation(testProgram, "u_color"); + htestBgColor = GLES20.glGetUniformLocation(testProgram, "u_bgcolor"); + htestScale = GLES20.glGetUniformLocation(testProgram, "u_scale"); + htestVertexPosition0 = GLES20.glGetAttribLocation(testProgram, "a_pos0"); htestVertexPosition1 = GLES20.glGetAttribLocation(testProgram, "a_pos1"); + htestVertexLength0 = GLES20.glGetAttribLocation(testProgram, "a_len0"); + htestVertexLength1 = GLES20.glGetAttribLocation(testProgram, "a_len1"); htestVertexFlip = GLES20.glGetAttribLocation(testProgram, "a_flip"); int[] mVboIds = new int[3]; @@ -186,34 +171,37 @@ public class TestLineOverlay extends RenderOverlay { mVertexFlipID = mVboIds[2]; float points[] = { - 800, 0, - 0, 0, - //-400, 100, - //-600, 200, - //-800, 100, + -800, -800, + 800, -800, + 800, 800, + -800, 800, + -800, -800, }; // float[] points = new float[12 * 2]; // for (int i = 0; i < 24; i += 2) { - // points[i + 0] = (float) Math.sin(-i / 11f * Math.PI) * 400; - // points[i + 1] = (float) Math.cos(-i / 11f * Math.PI) * 400; + // points[i + 0] = (float) Math.sin(-i / 11f * Math.PI) * 8*400; + // points[i + 1] = (float) Math.cos(-i / 11f * Math.PI) * 8*400; // } - mNumVertices = (points.length - 2) * 2; + boolean oddSegments = points.length % 4 == 0; - short[] vertices = new short[(mNumVertices + 2) * 4]; + int numVertices = points.length + (oddSegments ? 2 : 0); - int opos = 4; + short[] vertices = new short[numVertices * 6]; + + int opos = 6; float x = points[0]; float y = points[1]; - float scale = 127; + float scale = 255; boolean even = true; + float len = 0; - for (int i = 2; i < points.length;) { - float nx = points[i++]; - float ny = points[i++]; + for (int i = 2; i < points.length; i += 2) { + float nx = points[i + 0]; + float ny = points[i + 1]; // Calculate triangle corners for the given width float vx = nx - x; @@ -225,7 +213,7 @@ public class TestLineOverlay extends RenderOverlay { vx /= a; vy /= a; - // perpendicular + // perpendicular to line segment float ux = -vy; float uy = vx; @@ -236,33 +224,41 @@ public class TestLineOverlay extends RenderOverlay { vertices[opos + 1] = (short) y; vertices[opos + 2] = dx; vertices[opos + 3] = dy; + vertices[opos + 4] = (short) len; + vertices[opos + 5] = 0; - vertices[opos + 8] = (short) nx; - vertices[opos + 9] = (short) ny; - vertices[opos + 10] = dx; - vertices[opos + 11] = dy; + len += a; + vertices[opos + 12] = (short) nx; + vertices[opos + 13] = (short) ny; + vertices[opos + 14] = dx; + vertices[opos + 15] = dy; + vertices[opos + 16] = (short) len; + vertices[opos + 17] = 0; x = nx; y = ny; if (even) { - opos += 4; + // go to second segment + opos += 6; even = false; } else { + // go to next block even = true; - opos += 12; + opos += 18; } } - flip = new byte[(points.length - 2)]; + // 0, 1, 0, 1 + byte[] flip = new byte[points.length]; for (int i = 0; i < flip.length; i++) flip[i] = (byte) (i % 2); short j = 0; mNumIndices = ((points.length) >> 2) * 6; - indices = new short[mNumIndices]; + short[] indices = new short[mNumIndices]; for (int i = 0; i < mNumIndices; i += 6, j += 4) { indices[i + 0] = (short) (j + 0); indices[i + 1] = (short) (j + 1); @@ -273,7 +269,7 @@ public class TestLineOverlay extends RenderOverlay { indices[i + 5] = (short) (j + 3); } - ByteBuffer buf = ByteBuffer.allocateDirect(128 * 4) + ByteBuffer buf = ByteBuffer.allocateDirect(numVertices * 6 * 2) .order(ByteOrder.nativeOrder()); ShortBuffer sbuf = buf.asShortBuffer(); @@ -285,7 +281,6 @@ public class TestLineOverlay extends RenderOverlay { GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); sbuf.clear(); - //sbuf.put(box); sbuf.put(vertices); sbuf.flip(); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBufferID); @@ -300,12 +295,23 @@ public class TestLineOverlay extends RenderOverlay { GLES20.GL_STATIC_DRAW); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + byte[] stipple = new byte[2]; + stipple[0] = 8; + stipple[1] = 8; + //stipple[2] = 16; + //stipple[3] = 48; + + mTexID = GlUtils.loadStippleTexture(stipple); + mMapView.getMapViewPosition().getMapPosition(mMapPosition); // tell GLRenderer to call 'render' isReady = true; } + private final static int STRIDE = 12; + private final static int LEN_OFFSET = 8; + @Override public synchronized void render(MapPosition pos, Matrices m) { @@ -318,42 +324,79 @@ public class TestLineOverlay extends RenderOverlay { GLState.enableVertexArrays(-1, -1); GLES20.glEnableVertexAttribArray(htestVertexPosition0); GLES20.glEnableVertexAttribArray(htestVertexPosition1); - + GlUtils.checkGlError("-4"); + GLES20.glEnableVertexAttribArray(htestVertexLength0); + GlUtils.checkGlError("-3"); + GLES20.glEnableVertexAttribArray(htestVertexLength1); + GlUtils.checkGlError("-2"); GLES20.glEnableVertexAttribArray(htestVertexFlip); GLES20.glUniformMatrix4fv(htestMatrix, 1, false, m.mvp, 0); + float div = FastMath.pow(pos.zoomLevel - mMapPosition.zoomLevel); + GLES20.glUniform1f(htestScale, pos.scale / mMapPosition.scale * div); - GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferID); + GLES20.glUniform4f(htestTexColor, 1.0f, 1.0f, 1.0f, 1.0f); + GLES20.glUniform4f(htestBgColor, 0.3f, 0.3f, 0.3f, 1.0f); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexID); + + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, + mIndicesBufferID); + GlUtils.checkGlError("-1"); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexFlipID); - GLES20.glVertexAttribPointer(htestVertexFlip, 1, GLES20.GL_BYTE, false, 0, 0); - + GLES20.glVertexAttribPointer(htestVertexFlip, 1, + GLES20.GL_BYTE, false, 0, 0); + GlUtils.checkGlError("0"); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBufferID); + GlUtils.checkGlError("1"); + // first pass GLES20.glVertexAttribPointer(htestVertexPosition0, - 4, GLES20.GL_SHORT, false, 0, 8); + 4, GLES20.GL_SHORT, false, STRIDE, STRIDE); + GlUtils.checkGlError("2"); + + GLES20.glVertexAttribPointer(htestVertexLength0, + 2, GLES20.GL_SHORT, false, STRIDE, STRIDE + LEN_OFFSET); + GlUtils.checkGlError("3"); GLES20.glVertexAttribPointer(htestVertexPosition1, - 4, GLES20.GL_SHORT, false, 0, 0); + 4, GLES20.GL_SHORT, false, STRIDE, 0); + GlUtils.checkGlError("4"); - GLES20.glUniform4f(htestColor, 0.5f, 0.5f, 1.0f, 1.0f); - GLES20.glDrawElements(GLES20.GL_TRIANGLES, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0); + GLES20.glVertexAttribPointer(htestVertexLength1, + 2, GLES20.GL_SHORT, false, STRIDE, LEN_OFFSET); + GlUtils.checkGlError("5"); + //GLES20.glUniform4f(htestColor, 0.5f, 0.5f, 1.0f, 1.0f); + GLES20.glDrawElements(GLES20.GL_TRIANGLES, mNumIndices, + GLES20.GL_UNSIGNED_SHORT, 0); + + // second pass GLES20.glVertexAttribPointer(htestVertexPosition0, - 4, GLES20.GL_SHORT, false, 0, 16); + 4, GLES20.GL_SHORT, false, STRIDE, 2 * STRIDE); + + GLES20.glVertexAttribPointer(htestVertexLength0, + 2, GLES20.GL_SHORT, false, STRIDE, 2 * STRIDE + LEN_OFFSET); GLES20.glVertexAttribPointer(htestVertexPosition1, - 4, GLES20.GL_SHORT, false, 0, 8); + 4, GLES20.GL_SHORT, false, STRIDE, STRIDE); - GLES20.glUniform4f(htestColor, 0.5f, 1.0f, 0.5f, 1.0f); - GLES20.glDrawElements(GLES20.GL_TRIANGLES, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0); + GLES20.glVertexAttribPointer(htestVertexLength1, + 2, GLES20.GL_SHORT, false, STRIDE, STRIDE + LEN_OFFSET); + + GLES20.glDrawElements(GLES20.GL_TRIANGLES, mNumIndices, + GLES20.GL_UNSIGNED_SHORT, 0); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); GLES20.glDisableVertexAttribArray(htestVertexPosition0); GLES20.glDisableVertexAttribArray(htestVertexPosition1); + GLES20.glDisableVertexAttribArray(htestVertexLength0); + GLES20.glDisableVertexAttribArray(htestVertexLength1); GLES20.glDisableVertexAttribArray(htestVertexFlip); GlUtils.checkGlError("..."); + + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); } @Override diff --git a/src/org/oscim/renderer/overlays/TestOverlay.java b/src/org/oscim/renderer/overlays/TestOverlay.java index 75199392..62ecbb9c 100644 --- a/src/org/oscim/renderer/overlays/TestOverlay.java +++ b/src/org/oscim/renderer/overlays/TestOverlay.java @@ -14,13 +14,10 @@ */ package org.oscim.renderer.overlays; -import java.io.IOException; - import org.oscim.core.MapPosition; -import org.oscim.renderer.layer.SymbolItem; -import org.oscim.renderer.layer.SymbolLayer; +import org.oscim.renderer.layer.Layer; +import org.oscim.renderer.layer.LineTexLayer; import org.oscim.renderer.layer.TextItem; -import org.oscim.theme.renderinstruction.BitmapUtils; import org.oscim.view.MapView; public class TestOverlay extends BasicOverlay { @@ -34,12 +31,33 @@ public class TestOverlay extends BasicOverlay { public TestOverlay(MapView mapView) { super(mapView); - // LineLayer ll = (LineLayer) layers.getLayer(1, Layer.LINE); - // ll.line = new Line(Color.BLUE, 1.0f, Cap.BUTT); - // ll.width = 2; - // float[] points = { -100, -100, 100, -100, 100, 100, -100, 100, -100, -100 }; - // short[] index = { (short) points.length }; - // ll.addLine(points, index, false); + // draw a rectangle + //LineLayer ll = (LineLayer) layers.getLayer(1, Layer.LINE); + //ll.line = new Line(Color.BLUE, 1.0f, Cap.BUTT); + //ll.width = 2; + float[] points = { + -100, -100, + 100, -100, + 100, 100, + -100, 100, + -100, -100 + }; + //short[] index = { (short) points.length }; + //ll.addLine(points, index, true); + + + LineTexLayer lt = (LineTexLayer) layers.getLayer(2, Layer.TEXLINE); + lt.addLine(points, null); + + float[] points2 = { + -200, -200, + 200, -200, + 200, 200, + -200, 200, + -200, -200 + }; + + lt.addLine(points2, null); // // PolygonLayer pl = (PolygonLayer) layers.getLayer(0, Layer.POLYGON); @@ -54,33 +72,33 @@ public class TestOverlay extends BasicOverlay { // short[] pindex = { (short) ppoints.length }; // pl.addPolygon(ppoints, pindex); - SymbolLayer sl = new SymbolLayer(); - SymbolItem it = new SymbolItem(); - - it.x = 0; - it.y = 0; - // billboard always faces camera - it.billboard = true; - - try { - it.bitmap = BitmapUtils.createBitmap("file:/sdcard/cheshire.png"); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - sl.addSymbol(it); - - SymbolItem it2 = new SymbolItem(); - it2.bitmap = it.bitmap; - it2.x = 0; - it2.y = 0; - // billboard always faces camera - it2.billboard = false; - - sl.addSymbol(it2); - sl.fixed = false; - - layers.textureLayers = sl; + //SymbolLayer sl = new SymbolLayer(); + //SymbolItem it = new SymbolItem(); + // + //it.x = 0; + //it.y = 0; + //// billboard always faces camera + //it.billboard = true; + // + //try { + // it.bitmap = BitmapUtils.createBitmap("file:/sdcard/cheshire.png"); + //} catch (IOException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + //} + //sl.addSymbol(it); + // + //SymbolItem it2 = new SymbolItem(); + //it2.bitmap = it.bitmap; + //it2.x = 0; + //it2.y = 0; + //// billboard always faces camera + //it2.billboard = false; + // + //sl.addSymbol(it2); + //sl.fixed = false; + // + //layers.textureLayers = sl; // TextLayer tl = new TextLayer(); // Text t = Text.createText(20, 2, Color.WHITE, Color.BLACK, false); @@ -106,7 +124,7 @@ public class TestOverlay extends BasicOverlay { updateMapPosition(); first = false; - ((SymbolLayer) (layers.textureLayers)).prepare(); + //((SymbolLayer) (layers.textureLayers)).prepare(); // pass layers to be uploaded and drawn to GL Thread // afterwards never modify 'layers' outside of this function! diff --git a/src/org/oscim/renderer/overlays/TextOverlay.java b/src/org/oscim/renderer/overlays/TextOverlay.java index f9a1125e..57626af5 100644 --- a/src/org/oscim/renderer/overlays/TextOverlay.java +++ b/src/org/oscim/renderer/overlays/TextOverlay.java @@ -386,8 +386,8 @@ public class TextOverlay extends BasicOverlay { layers.clear(); if (mDebugLayer != null) { - layers.layers = mDebugLayer.layers; - mDebugLayer.layers = null; + layers.baseLayers = mDebugLayer.baseLayers; + mDebugLayer.baseLayers = null; } // set new TextLayer to be uploaded and used diff --git a/src/org/oscim/renderer/overlays/TextOverlayExp.java b/src/org/oscim/renderer/overlays/TextOverlayExp.java index 9acfa117..14901a29 100644 --- a/src/org/oscim/renderer/overlays/TextOverlayExp.java +++ b/src/org/oscim/renderer/overlays/TextOverlayExp.java @@ -627,7 +627,7 @@ public class TextOverlayExp extends BasicOverlay { layers.clear(); if (mDebugLayer != null) { - layers.layers = mDebugLayer.layers; + layers.baseLayers = mDebugLayer.baseLayers; mDebugLayer = null; } @@ -665,12 +665,12 @@ public class TextOverlayExp extends BasicOverlay { return; } - if (vbo == null) { - vbo = BufferObject.get(0); + if (layers.vbo == null) { + layers.vbo = BufferObject.get(0); } if (newSize > 0) { - if (GLRenderer.uploadLayers(layers, vbo, newSize, true)) + if (GLRenderer.uploadLayers(layers, newSize, true)) isReady = true; } } @@ -679,19 +679,19 @@ public class TextOverlayExp extends BasicOverlay { public synchronized void render(MapPosition pos, Matrices m) { float div = FastMath.pow(mMapPosition.zoomLevel - pos.zoomLevel); - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo.id); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, layers.vbo.id); GLState.test(false, false); - if (layers.layers != null) { + if (layers.baseLayers != null) { setMatrix(pos, m, true); //Matrix.multiplyMM(m.mvp, 0, m.proj, 0, m.mvp,0); - for (Layer l = layers.layers; l != null;) { + for (Layer l = layers.baseLayers; l != null;) { if (l.type == Layer.POLYGON) { l = PolygonRenderer.draw(pos, l, m.mvp, true, false); } else { float scale = pos.scale * div; - l = LineRenderer.draw(pos, l, m.mvp, scale, 0, layers.lineOffset); + l = LineRenderer.draw(layers, l, pos, m.mvp, scale, 0); } } } diff --git a/src/org/oscim/theme/renderinstruction/Line.java b/src/org/oscim/theme/renderinstruction/Line.java index a89f1f96..8b989578 100644 --- a/src/org/oscim/theme/renderinstruction/Line.java +++ b/src/org/oscim/theme/renderinstruction/Line.java @@ -229,35 +229,13 @@ public final class Line extends RenderInstruction { this.fade = fade; this.stipple = stipple; this.min = min; + + if (stipple != 0){ + System.out.println("a"); + } } - /** - * @param line - * ... - * @param style - * ... - * @param src - * ... - * @param stroke - * ... - * @param strokeWidth - * ... - * @param stipple - * ... - * @param strokeLinecap - * ... - * @param level - * ... - * @param fixed - * ... - * @param fade - * ... - * @param blur - * ... - * @param isOutline - * ... - * @param min ... - */ + private Line(Line line, String style, String src, int stroke, float strokeWidth, int stipple, Cap strokeLinecap, int level, boolean fixed, int fade, float blur, boolean isOutline, float min) { diff --git a/src/org/oscim/utils/FastMath.java b/src/org/oscim/utils/FastMath.java index e5bb1bd6..a20a3c2a 100644 --- a/src/org/oscim/utils/FastMath.java +++ b/src/org/oscim/utils/FastMath.java @@ -54,6 +54,13 @@ public class FastMath { return (pow > 0 ? (1 << pow) : (1.0f / (1 << -pow))); } + public static int clamp(int value, int max, int min){ + return (value < min ? min : (value > max ? max : value)); + } + + public static byte clampToByte(int value){ + return (byte)(value < 0 ? 0 : (value > 255 ? 255 : value)); + } public static float abs(float value){ return value < 0 ? -value : value; diff --git a/src/org/oscim/utils/GlUtils.java b/src/org/oscim/utils/GlUtils.java index b7e1971b..466f5a0f 100644 --- a/src/org/oscim/utils/GlUtils.java +++ b/src/org/oscim/utils/GlUtils.java @@ -30,10 +30,25 @@ import android.util.Log; public class GlUtils { private static String TAG = "GlUtils"; + public static void setTextureParameter(int min_filter, int mag_filter, int wrap_s, int wrap_t) { + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_MIN_FILTER, + min_filter); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_MAG_FILTER, + mag_filter); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_WRAP_S, + wrap_s); // Set U Wrapping + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_WRAP_T, + wrap_t); // Set V Wrapping + } + /** * @param bitmap * ... - * @return gl identifier + * @return textureId */ public static int loadTextures(Bitmap bitmap) { @@ -44,21 +59,8 @@ public class GlUtils { GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID); - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, - GLES20.GL_TEXTURE_MIN_FILTER, - GLES20.GL_LINEAR); - - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, - GLES20.GL_TEXTURE_MAG_FILTER, - GLES20.GL_LINEAR); - - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, - GLES20.GL_TEXTURE_WRAP_S, - GLES20.GL_CLAMP_TO_EDGE); - - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, - GLES20.GL_TEXTURE_WRAP_T, - GLES20.GL_CLAMP_TO_EDGE); + setTextureParameter(GLES20.GL_LINEAR, GLES20.GL_LINEAR, + GLES20.GL_CLAMP_TO_EDGE, GLES20.GL_CLAMP_TO_EDGE); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); @@ -66,24 +68,13 @@ public class GlUtils { } public static int loadTexture(byte[] pixel, int width, int height, int format, - int wrap_s, int wrap_t) { + int min_filter, int mag_filter, int wrap_s, int wrap_t) { int[] textureIds = new int[1]; GLES20.glGenTextures(1, textureIds, 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]); - 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, - wrap_s); // Set U Wrapping - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, - GLES20.GL_TEXTURE_WRAP_T, - wrap_t); // Set V Wrapping + setTextureParameter(min_filter, mag_filter, wrap_s, wrap_t); ByteBuffer buf = ByteBuffer.allocateDirect(width * height).order(ByteOrder.nativeOrder()); buf.put(pixel); @@ -96,6 +87,37 @@ public class GlUtils { return textureIds[0]; } + public static int loadStippleTexture(byte[] stipple) { + int sum = 0; + for (byte flip : stipple) + sum += flip; + + byte[] pixel = new byte[sum]; + + boolean on = true; + int pos = 0; + for (byte flip : stipple) { + float max = flip; + + for (int s = 0; s < flip; s++) { + float color = Math.abs(s / (max - 1) - 0.5f); + if (on) + color = 255 * (1 - color); + else + color = 255 * color; + + pixel[pos + s] = FastMath.clampToByte((int) color); + } + on = !on; + pos += flip; + } + + return loadTexture(pixel, sum, 1, GLES20.GL_ALPHA, + GLES20.GL_LINEAR, GLES20.GL_LINEAR, + //GLES20.GL_NEAREST, GLES20.GL_NEAREST, + GLES20.GL_REPEAT, GLES20.GL_REPEAT); + } + /** * @param shaderType * shader type