- add initial version of line stipple renderer

- 'vbo' moved to 'Layers'
This commit is contained in:
Hannes Janetzek 2013-02-22 05:30:24 +01:00
parent a0083ae484
commit 7cf4ca27f3
23 changed files with 1148 additions and 449 deletions

View File

@ -16,6 +16,8 @@ package org.oscim.generator;
import static org.oscim.generator.JobTile.STATE_NONE; import static org.oscim.generator.JobTile.STATE_NONE;
import java.util.Arrays;
import org.oscim.core.MercatorProjection; import org.oscim.core.MercatorProjection;
import org.oscim.core.Tag; import org.oscim.core.Tag;
import org.oscim.core.Tile; 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.Layer;
import org.oscim.renderer.layer.Layers; import org.oscim.renderer.layer.Layers;
import org.oscim.renderer.layer.LineLayer; import org.oscim.renderer.layer.LineLayer;
import org.oscim.renderer.layer.LineTexLayer;
import org.oscim.renderer.layer.PolygonLayer; import org.oscim.renderer.layer.PolygonLayer;
import org.oscim.renderer.layer.TextItem; import org.oscim.renderer.layer.TextItem;
import org.oscim.theme.IRenderCallback; import org.oscim.theme.IRenderCallback;
@ -254,6 +257,8 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
mTagHouseNr = tags[i]; mTagHouseNr = tags[i];
tags[i] = mTagEmptyHouseNr; tags[i] = mTagEmptyHouseNr;
} else if (mTile.zoomLevel >= 17 && } else if (mTile.zoomLevel >= 17 &&
// FIXME, allow overlays to intercept
// this, or use a theme option for this
key == Tag.TAG_KEY_BUILDING) { key == Tag.TAG_KEY_BUILDING) {
mRenderBuildingModel = true; mRenderBuildingModel = true;
} }
@ -270,38 +275,12 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
mTagName = null; mTagName = null;
mTagHouseNr = 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; mPoiX = longitude;
mPoiY = latitude; mPoiY = latitude;
//}
// remove tags that should not be cached in Rendertheme // remove tags that should not be cached in Rendertheme
filterTags(tags); filterTags(tags);
// Log.d(TAG, "renderPointOfInterest: " + mTagName);
// mNodeRenderInstructions =
TileGenerator.renderTheme.matchNode(this, tags, mTile.zoomLevel); TileGenerator.renderTheme.matchNode(this, tags, mTile.zoomLevel);
} }
@ -338,8 +317,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
private void debugUnmatched(boolean closed, Tag[] tags) { private void debugUnmatched(boolean closed, Tag[] tags) {
Log.d(TAG, "way not matched: " + tags[0] + " " Log.d(TAG, "DBG way not matched: " + closed + " " + Arrays.deepToString(tags));
+ (tags.length > 1 ? tags[1] : "") + " " + closed);
mTagName = new Tag("name", tags[0].key + ":" + tags[0].value, false); mTagName = new Tag("name", tags[0].key + ":" + tags[0].value, false);
@ -366,16 +344,20 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
// ----------------- RenderThemeCallback ----------------- // ----------------- RenderThemeCallback -----------------
@Override @Override
public void renderWay(Line line, int level) { 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; int numLayer = (mDrawingLayer * 2) + level;
LineLayer lineLayer = (LineLayer) mLayers.getLayer(numLayer, Layer.LINE); 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 lineLayer = (LineLayer)
mLayers.getLayer(numLayer, Layer.LINE);
if (lineLayer == null) if (lineLayer == null)
return; return;
@ -397,6 +379,27 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
lineLayer.addLine(mCoords, mIndices, mClosed); lineLayer.addLine(mCoords, mIndices, mClosed);
mCurLineLayer = lineLayer; 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 @Override
@ -520,6 +523,31 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
// // TODO move this to Projection classes // // 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 String mMapProjection;
// private static final double PI180 = (Math.PI / 180) / 1000000.0; // private static final double PI180 = (Math.PI / 180) / 1000000.0;
// private static final double PIx4 = Math.PI * 4; // private static final double PIx4 = Math.PI * 4;

View File

@ -28,7 +28,8 @@ import android.opengl.GLES20;
import android.opengl.Matrix; 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 * visible tiles that do not have data available yet its parent in children
* tiles are rendered when available. * tiles are rendered when available.
* *
@ -107,12 +108,12 @@ public class BaseMap {
if (t.holder != null) if (t.holder != null)
t = t.holder; 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)); //Log.d(TAG, "missing data " + (t.layers == null) + " " + (t.vbo == null));
return; return;
} }
GLES20.glBindBuffer(GL_ARRAY_BUFFER, t.vbo.id); GLES20.glBindBuffer(GL_ARRAY_BUFFER, t.layers.vbo.id);
// place tile relative to map position // place tile relative to map position
float div = FastMath.pow(tile.zoomLevel - pos.zoomLevel); float div = FastMath.pow(tile.zoomLevel - pos.zoomLevel);
@ -135,8 +136,9 @@ public class BaseMap {
int simpleShader = (pos.tilt < 1 ? 1 : 0); int simpleShader = (pos.tilt < 1 ? 1 : 0);
boolean clipped = false; 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) { switch (l.type) {
case Layer.POLYGON: case Layer.POLYGON:
l = PolygonRenderer.draw(pos, l, mvp, !clipped, true); l = PolygonRenderer.draw(pos, l, mvp, !clipped, true);
@ -144,16 +146,38 @@ public class BaseMap {
break; break;
case Layer.LINE: case Layer.LINE:
//if (!lineTexture) {
LineRenderer.beginLines();
// lineTexture = true;
//}
if (!clipped) { if (!clipped) {
// draw stencil buffer clip region
PolygonRenderer.draw(pos, null, mvp, true, true); PolygonRenderer.draw(pos, null, mvp, true, true);
clipped = true; clipped = true;
} }
l = LineRenderer.draw(pos, l, mvp, div, simpleShader, l = LineRenderer.draw(t.layers, l, pos, mvp, div, simpleShader);
t.layers.lineOffset);
break; 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); PolygonRenderer.drawOver(mvp);
} }

View File

@ -74,7 +74,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
// bytes currently loaded in VBOs // bytes currently loaded in VBOs
private static int mBufferMemoryUsage; private static int mBufferMemoryUsage;
private static float[] mTileCoords = new float[8]; private static float[] mTileCoords = new float[8];
private static float[] mDebugCoords = 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; 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) { boolean addFill) {
GLES20.glBindBuffer(GL_ARRAY_BUFFER, vbo.id); GLES20.glBindBuffer(GL_ARRAY_BUFFER, layers.vbo.id);
// add fill coordinates // add fill coordinates
if (addFill) if (addFill)
@ -241,23 +240,23 @@ public class GLRenderer implements GLSurfaceView.Renderer {
if (newSize != sbuf.remaining()) { if (newSize != sbuf.remaining()) {
Log.d(TAG, "wrong size: " Log.d(TAG, "wrong size: "
+ newSize + " " + " new size: " + newSize
+ sbuf.position() + " " + " buffer pos: " + sbuf.position()
+ sbuf.limit() + " " + " buffer limit: " + sbuf.limit()
+ sbuf.remaining()); + " buffer fill: " + sbuf.remaining());
return false; return false;
} }
newSize *= SHORT_BYTES; newSize *= SHORT_BYTES;
// reuse memory allocated for vbo when possible and allocated // reuse memory allocated for vbo when possible and allocated
// memory is less then four times the new data // 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) { && mBufferMemoryUsage < LIMIT_BUFFERS) {
GLES20.glBufferSubData(GL_ARRAY_BUFFER, 0, newSize, sbuf); GLES20.glBufferSubData(GL_ARRAY_BUFFER, 0, newSize, sbuf);
} else { } else {
mBufferMemoryUsage += newSize - vbo.size; mBufferMemoryUsage += newSize - layers.vbo.size;
vbo.size = newSize; layers.vbo.size = newSize;
GLES20.glBufferData(GL_ARRAY_BUFFER, vbo.size, sbuf, GL_DYNAMIC_DRAW); GLES20.glBufferData(GL_ARRAY_BUFFER, layers.vbo.size, sbuf, GL_DYNAMIC_DRAW);
} }
return true; return true;
@ -272,15 +271,16 @@ public class GLRenderer implements GLSurfaceView.Renderer {
int newSize = tile.layers.getSize(); int newSize = tile.layers.getSize();
if (newSize > 0) { if (newSize > 0) {
if (tile.vbo == null) if (tile.layers.vbo == null)
tile.vbo = BufferObject.get(newSize); 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!"); Log.d(TAG, "BUG uploadTileData " + tile + " failed!");
BufferObject.release(tile.layers.vbo);
tile.layers.vbo = null;
tile.layers.clear(); tile.layers.clear();
tile.layers = null; tile.layers = null;
BufferObject.release(tile.vbo);
tile.vbo = null;
} }
} }
} }
@ -621,7 +621,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
// String ext = GLES20.glGetString(GLES20.GL_EXTENSIONS); // String ext = GLES20.glGetString(GLES20.GL_EXTENSIONS);
// Log.d(TAG, "Extensions: " + ext); // Log.d(TAG, "Extensions: " + ext);
// classes that require GL context for initialization
LineRenderer.init(); LineRenderer.init();
LineTexRenderer.init();
PolygonRenderer.init(); PolygonRenderer.init();
TextureRenderer.init(); TextureRenderer.init();
TextureObject.init(10); TextureObject.init(10);

View File

@ -27,6 +27,7 @@ import static android.opengl.GLES20.glVertexAttribPointer;
import org.oscim.core.MapPosition; import org.oscim.core.MapPosition;
import org.oscim.generator.TileGenerator; import org.oscim.generator.TileGenerator;
import org.oscim.renderer.layer.Layer; import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.Layers;
import org.oscim.renderer.layer.LineLayer; import org.oscim.renderer.layer.LineLayer;
import org.oscim.theme.renderinstruction.Line; import org.oscim.theme.renderinstruction.Line;
import org.oscim.utils.GlUtils; import org.oscim.utils.GlUtils;
@ -34,10 +35,6 @@ import org.oscim.utils.GlUtils;
import android.opengl.GLES20; import android.opengl.GLES20;
import android.util.Log; import android.util.Log;
/**
* @author Hannes Janetzek
*/
public final class LineRenderer { public final class LineRenderer {
private final static String TAG = LineRenderer.class.getName(); 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, mTexID = GlUtils.loadTexture(pixel, 128, 128, GLES20.GL_ALPHA,
GLES20.GL_NEAREST, GLES20.GL_NEAREST,
GLES20.GL_MIRRORED_REPEAT, GLES20.GL_MIRRORED_REPEAT); GLES20.GL_MIRRORED_REPEAT, GLES20.GL_MIRRORED_REPEAT);
return true; return true;
@ -110,13 +108,13 @@ public final class LineRenderer {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
} }
public static Layer draw(MapPosition pos, Layer layer, float[] matrix, float div, public static Layer draw(Layers layers, Layer curLayer, MapPosition pos,
int mode, int bufferOffset) { float[] matrix, float div, int mode) {
int zoom = pos.zoomLevel; int zoom = pos.zoomLevel;
float scale = pos.scale; float scale = pos.scale;
if (layer == null) if (curLayer == null)
return null; return null;
GLState.blend(true); GLState.blend(true);
@ -131,13 +129,13 @@ public final class LineRenderer {
GLState.enableVertexArrays(hLineVertexPosition[mode], -1); GLState.enableVertexArrays(hLineVertexPosition[mode], -1);
glVertexAttribPointer(hLineVertexPosition[mode], 4, GL_SHORT, 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); glUniformMatrix4fv(hLineMatrix[mode], 1, false, matrix, 0);
// line scale factor for non fixed lines: within a zoom- // Line scale factor for non fixed lines: Within a zoom-
// level lines would be scaled by the factor 2 via projection. // level lines would be scaled by the factor 2 by view-matrix.
// though lines should only scale by sqrt(2). this is achieved // Though lines should only scale by sqrt(2). This is achieved
// by inverting scaling of extrusion vector with: width/sqrt(s). // by inverting scaling of extrusion vector with: width/sqrt(s).
// within one zoom-level: 1 <= s <= 2 // within one zoom-level: 1 <= s <= 2
float s = scale / div; float s = scale / div;
@ -158,7 +156,7 @@ public final class LineRenderer {
// dont increase scale when max is reached // dont increase scale when max is reached
boolean strokeMaxZoom = zoom > TileGenerator.STROKE_MAX_ZOOM_LEVEL; boolean strokeMaxZoom = zoom > TileGenerator.STROKE_MAX_ZOOM_LEVEL;
Layer l = layer; Layer l = curLayer;
for (; l != null && l.type == Layer.LINE; l = l.next) { for (; l != null && l.type == Layer.LINE; l = l.next) {
LineLayer ll = (LineLayer) l; LineLayer ll = (LineLayer) l;
Line line = ll.line; Line line = ll.line;
@ -187,7 +185,7 @@ public final class LineRenderer {
} else { } else {
width = ll.width / s + o.width / lineScale; 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) if (o.line.min > 0 && o.width * lineScale < o.line.min * 2)
continue; continue;
} }
@ -223,7 +221,9 @@ public final class LineRenderer {
// line width increases by sqrt(2.2). // line width increases by sqrt(2.2).
width = ll.width / lineScale; 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; width = (ll.width - 0.2f) / lineScale;
} }
@ -262,7 +262,6 @@ public final class LineRenderer {
+ "attribute vec4 a_pos;" + "attribute vec4 a_pos;"
+ "uniform float u_mode;" + "uniform float u_mode;"
+ "varying vec2 v_st;" + "varying vec2 v_st;"
+ "varying vec2 v_mode;"
+ "void main() {" + "void main() {"
// scale extrusion to u_width pixel // scale extrusion to u_width pixel
// just ignore the two most insignificant bits of a_st :) // 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 // last two bits of a_st hold the texture coordinates
// ..maybe one could wrap texture so that `abs` is not required // ..maybe one could wrap texture so that `abs` is not required
+ " v_st = abs(mod(dir, 4.0)) - 1.0;" + " v_st = abs(mod(dir, 4.0)) - 1.0;"
+ " v_mode = vec2(1.0 - u_mode, u_mode);"
+ "}"; + "}";
private final static String lineSimpleFragmentShader = "" private final static String lineSimpleFragmentShader = ""
+ "precision mediump float;" + "precision mediump float;"
+ "uniform sampler2D tex;" + "uniform sampler2D tex;"
+ "uniform float u_wscale;" + "uniform float u_wscale;"
+ "uniform float u_mode;"
+ "uniform vec4 u_color;" + "uniform vec4 u_color;"
+ "varying vec2 v_st;" + "varying vec2 v_st;"
+ "varying vec2 v_mode;"
+ "void main() {" + "void main() {"
//+ " float len;" //+ " float len;"
// some say one should not use conditionals // (currently required as overlay line renderers dont load the texture)
// (FIXME currently required as overlay line renderers dont load the texture)
//+ " if (u_mode == 0)" //+ " if (u_mode == 0)"
//+ " len = abs(v_st.s);" //+ " len = abs(v_st.s);"
//+ " else" //+ " else"
//+ " len = texture2D(tex, v_st).a;" //+ " len = texture2D(tex, v_st).a;"
// one trick to avoid branching, need to check performance // this avoids branching, need to check performance
+ " float len = max(v_mode[0] * abs(v_st.s), v_mode[1] * texture2D(tex, v_st).a);" + " 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 // interpolate alpha between: 0.0 < 1.0 - len < u_wscale
// where wscale is 'filter width' / 'line width' and 0 <= len <= sqrt(2) // 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 * smoothstep(0.0, u_wscale, 1.0 - len);"

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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)));"
+ "}";
}

View File

@ -37,15 +37,6 @@ public final class MapTile extends JobTile {
*/ */
public boolean isVisible; 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 * Pointer to access relatives in QuadTree
*/ */

View File

@ -390,17 +390,18 @@ public class TileManager {
return; return;
if (t.layers != null) { 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.clear();
t.layers = null; t.layers = null;
} }
TextItem.release(t.labels); TextItem.release(t.labels);
if (t.vbo != null) {
BufferObject.release(t.vbo);
t.vbo = null;
}
QuadTree.remove(t); QuadTree.remove(t);
t.state = STATE_NONE; t.state = STATE_NONE;
@ -572,11 +573,11 @@ public class TileManager {
return true; return true;
} }
if (tile.vbo != null) { //if (tile.vbo != null) {
// BAD Things(tm) happend: tile is already loaded // // BAD Things(tm) happend: tile is already loaded
Log.d(TAG, "BUG: tile loaded before " + tile); // Log.d(TAG, "BUG: tile loaded before " + tile);
return true; // return true;
} //}
tile.state = STATE_NEW_DATA; tile.state = STATE_NEW_DATA;
mTilesForUpload++; mTilesForUpload++;

View File

@ -64,7 +64,7 @@ public class ExtrusionLayer extends Layer {
public ExtrusionLayer(int level) { public ExtrusionLayer(int level) {
this.type = Layer.EXTRUSION; this.type = Layer.EXTRUSION;
this.layer = level; this.level = level;
mVertices = mCurVertices = VertexPool.get(); mVertices = mCurVertices = VertexPool.get();
@ -346,6 +346,7 @@ public class ExtrusionLayer extends Layer {
return convex; return convex;
} }
@Override
public void compile(ShortBuffer sbuf) { public void compile(ShortBuffer sbuf) {
if (mNumVertices == 0 || compiled) if (mNumVertices == 0 || compiled)

View File

@ -14,24 +14,28 @@
*/ */
package org.oscim.renderer.layer; package org.oscim.renderer.layer;
import java.nio.ShortBuffer;
/** /**
* @authorHannes Janetzek * @authorHannes Janetzek
*/ */
public abstract class Layer { public abstract class Layer {
public final static byte LINE = 0; public final static byte LINE = 0;
public final static byte POLYGON = 1; public final static byte POLYGON = 1;
public final static byte WAYTEXT = 2; public final static byte TEXLINE = 2;
public final static byte POITEXT = 3; public final static byte WAYTEXT = 3;
public final static byte SYMBOL = 4; public final static byte POITEXT = 4;
public final static byte BITMAP = 5; public final static byte SYMBOL = 5;
public final static byte TEXLINE = 6; public final static byte BITMAP = 6;
public final static byte EXTRUSION = 7; public final static byte EXTRUSION = 7;
public byte type = -1; public byte type = -1;
public Layer next; public Layer next;
int layer; // drawing order from bottom to top
int level;
// number of vertices for this layer // number of vertices for this layer
public int verticesCnt; public int verticesCnt;
@ -44,5 +48,6 @@ public abstract class Layer {
VertexPoolItem pool; VertexPoolItem pool;
protected VertexPoolItem curItem; protected VertexPoolItem curItem;
abstract protected void compile(ShortBuffer sbuf);
abstract protected void clear(); abstract protected void clear();
} }

View File

@ -16,22 +16,33 @@ package org.oscim.renderer.layer;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
import org.oscim.renderer.BufferObject;
import android.util.Log; import android.util.Log;
/**
* @author Hannes Janetzek
*/
public class Layers { public class Layers {
// mixed Polygon and Line layers private final static String TAG = Layers.class.getName();
public Layer layers;
// mixed Polygon- and LineLayer
public Layer baseLayers;
public Layer textureLayers; public Layer textureLayers;
public Layer extrusionLayers; 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: // To not need to switch VertexAttribPointer positions all the time:
// 1. polygons are packed in VBO at offset 0 // 1. polygons are packed in VBO at offset 0
// 2. lines afterwards at lineOffset // 2. lines afterwards at lineOffset
// 3. other layers keep their byte offset in Layer.offset // 3. other layers keep their byte offset in Layer.offset
public int lineOffset; public int lineOffset;
public int texLineOffset;
// time when layers became first rendered (in uptime) // time when layers became first rendered (in uptime)
// used for animations // used for animations
@ -39,93 +50,109 @@ public class Layers {
private Layer mCurLayer; 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) { public Layer getLayer(int level, byte type) {
Layer l = layers; Layer l = baseLayers;
Layer ret = null; Layer layer = null;
if (mCurLayer != null && mCurLayer.layer == level) { if (mCurLayer != null && mCurLayer.level == level) {
ret = mCurLayer; layer = mCurLayer;
} else if (l == null || l.layer > level) { } else {
if (l == null || l.level > level) {
// insert new layer at start // insert new layer at start
l = null; l = null;
} else { } else {
while (true) { while (true) {
if (l.layer == level) {
// found layer // found layer
ret = l; if (l.level == level) {
layer = l;
break; break;
} }
if (l.next == null || l.next.layer > level) {
// insert new layer between current and next layer // insert new layer between current and next layer
if (l.next == null || l.next.level > level)
break; break;
}
l = l.next; l = l.next;
} }
} }
if (ret == null) {
if (layer == null) {
// add a new Layer
if (type == Layer.LINE) if (type == Layer.LINE)
ret = new LineLayer(level); layer = new LineLayer(level);
else if (type == Layer.POLYGON) else if (type == Layer.POLYGON)
ret = new PolygonLayer(level); layer = new PolygonLayer(level);
else if (type == Layer.TEXLINE)
layer = new LineTexLayer(level);
else else
// TODO throw execption
return null; return null;
if (l == null) { if (l == null) {
// insert at start // insert at start
ret.next = layers; layer.next = baseLayers;
layers = ret; baseLayers = layer;
} else { } else {
ret.next = l.next; layer.next = l.next;
l.next = ret; l.next = layer;
} }
} 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 null;
} }
return ret; mCurLayer = layer;
return layer;
} }
private static int LINE_VERTEX_SHORTS = 4; private final static int[] VERTEX_SHORT_CNT = {
private static int POLY_VERTEX_SHORTS = 2; 4, // LINE_VERTEX_SHORTS
private static int TEXTURE_VERTEX_SHORTS = 6; 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() { public int getSize() {
int size = 0; int size = 0;
for (Layer l = layers; l != null; l = l.next) { for (Layer l = baseLayers; l != null; l = l.next)
if (l.type == Layer.LINE) size += l.verticesCnt * VERTEX_SHORT_CNT[l.type];
size += l.verticesCnt * LINE_VERTEX_SHORTS;
else
size += l.verticesCnt * POLY_VERTEX_SHORTS;
}
for (Layer l = textureLayers; l != null; l = l.next) for (Layer l = textureLayers; l != null; l = l.next)
size += l.verticesCnt * TEXTURE_VERTEX_SHORTS; size += l.verticesCnt * TEXTURE_VERTEX_SHORTS;
//for (Layer l = extrusionLayers; l != null; l = l.next)
// size += l.verticesCnt * EXTRUSION_VERTEX_SHORTS;
return size; return size;
} }
public void compile(ShortBuffer sbuf, boolean addFill) { public void compile(ShortBuffer sbuf, boolean addFill) {
// offset from fill coordinates // offset from fill coordinates
int pos = 0; int pos = 0;
if (addFill) int size = 0;
if (addFill){
pos = 4; pos = 4;
size = 8;
}
// add polygons first, needed to get the offsets right... size += addLayerItems(sbuf, baseLayers, Layer.POLYGON, pos);
addLayerItems(sbuf, layers, Layer.POLYGON, pos);
lineOffset = sbuf.position() * 2; // * short-bytes lineOffset = size * SHORT_BYTES;
addLayerItems(sbuf, layers, Layer.LINE, 0); 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) { for (Layer l = textureLayers; l != null; l = l.next) {
TextureLayer tl = (TextureLayer) l; 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 // optimization for Line- and PolygonLayer:
private static void addLayerItems(ShortBuffer sbuf, Layer l, byte type, int pos) { // 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; VertexPoolItem last = null, items = null;
int size = 0;
// HACK, see LineTexLayer
boolean addOffset = (type == Layer.TEXLINE);
for (; l != null; l = l.next) { for (; l != null; l = l.next) {
if (l.type != type) if (l.type != type)
continue; continue;
for (VertexPoolItem it = l.pool; it != null; it = it.next) { if (addOffset){
if (it.next == null) sbuf.position(sbuf.position() + 6);
sbuf.put(it.vertices, 0, it.used); addOffset = false;
else }
sbuf.put(it.vertices, 0, VertexPoolItem.SIZE);
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; last = it;
} }
if (last == null) if (last == null)
@ -169,11 +209,13 @@ public class Layers {
l.curItem = null; l.curItem = null;
} }
VertexPool.release(items); VertexPool.release(items);
return size;
} }
static void addPoolItems(Layer l, ShortBuffer sbuf) { static void addPoolItems(Layer l, ShortBuffer sbuf) {
// offset of layer data in vbo // 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) { for (VertexPoolItem it = l.pool; it != null; it = it.next) {
if (it.next == null) if (it.next == null)
@ -190,7 +232,7 @@ public class Layers {
public void clear() { public void clear() {
// clear line and polygon layers directly // clear line and polygon layers directly
Layer l = layers; Layer l = baseLayers;
while (l != null) { while (l != null) {
if (l.pool != null) { if (l.pool != null) {
VertexPool.release(l.pool); VertexPool.release(l.pool);
@ -207,8 +249,13 @@ public class Layers {
for (l = extrusionLayers; l != null; l = l.next) { for (l = extrusionLayers; l != null; l = l.next) {
l.clear(); l.clear();
} }
layers = null; baseLayers = null;
textureLayers = null; textureLayers = null;
extrusionLayers = null; extrusionLayers = null;
// if (vbo != null){
// BufferObject.release(vbo);
// vbo = null;
// }
} }
} }

View File

@ -14,6 +14,8 @@
*/ */
package org.oscim.renderer.layer; package org.oscim.renderer.layer;
import java.nio.ShortBuffer;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.renderer.GLRenderer; import org.oscim.renderer.GLRenderer;
import org.oscim.theme.renderinstruction.Line; import org.oscim.theme.renderinstruction.Line;
@ -26,7 +28,6 @@ import android.graphics.Paint.Cap;
* @author Hannes Janetzek * @author Hannes Janetzek
*/ */
public final class LineLayer extends Layer { public final class LineLayer extends Layer {
private static final float COORD_SCALE = GLRenderer.COORD_MULTIPLIER; private static final float COORD_SCALE = GLRenderer.COORD_MULTIPLIER;
// scale factor mapping extrusion vector to short values // scale factor mapping extrusion vector to short values
public static final float DIR_SCALE = 2048; public static final float DIR_SCALE = 2048;
@ -42,7 +43,7 @@ public final class LineLayer extends Layer {
public boolean roundCap; public boolean roundCap;
LineLayer(int layer) { LineLayer(int layer) {
this.layer = layer; this.level = layer;
this.type = Layer.LINE; this.type = Layer.LINE;
} }
@ -312,11 +313,13 @@ public final class LineLayer extends Layer {
vx *= -1; vx *= -1;
vy *= -1; vy *= -1;
int end = pos + length;
for (;;) { for (;;) {
if (ipos < pos + length) { if (ipos < end) {
nextX = points[ipos++]; nextX = points[ipos++];
nextY = points[ipos++]; nextY = points[ipos++];
} else if (closed && ipos < pos + length + 2) { } else if (closed && ipos < end + 2) {
// add startpoint == endpoint // add startpoint == endpoint
nextX = points[pos]; nextX = points[pos];
nextY = points[pos + 1]; nextY = points[pos + 1];
@ -543,4 +546,8 @@ public final class LineLayer extends Layer {
@Override @Override
protected void clear() { protected void clear() {
} }
@Override
protected void compile(ShortBuffer sbuf) {
}
} }

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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) {
}
}

View File

@ -14,6 +14,8 @@
*/ */
package org.oscim.renderer.layer; package org.oscim.renderer.layer;
import java.nio.ShortBuffer;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.renderer.GLRenderer; import org.oscim.renderer.GLRenderer;
import org.oscim.theme.renderinstruction.Area; import org.oscim.theme.renderinstruction.Area;
@ -24,7 +26,7 @@ public final class PolygonLayer extends Layer {
public Area area; public Area area;
PolygonLayer(int layer) { PolygonLayer(int layer) {
this.layer = layer; this.level = layer;
this.type = Layer.POLYGON; this.type = Layer.POLYGON;
curItem = VertexPool.get(); curItem = VertexPool.get();
pool = curItem; pool = curItem;
@ -87,6 +89,10 @@ public final class PolygonLayer extends Layer {
curItem = si; curItem = si;
} }
@Override
protected void compile(ShortBuffer sbuf) {
}
@Override @Override
protected void clear() { protected void clear() {
} }

View File

@ -32,7 +32,8 @@ public abstract class TextureLayer extends Layer {
* @param sbuf * @param sbuf
* buffer to add vertices * buffer to add vertices
*/ */
void compile(ShortBuffer sbuf) { @Override
protected void compile(ShortBuffer sbuf) {
for (TextureObject to = textures; to != null; to = to.next) for (TextureObject to = textures; to != null; to = to.next)
TextureObject.uploadTexture(to); TextureObject.uploadTexture(to);

View File

@ -22,7 +22,7 @@ public class VertexPoolItem {
// must be multiple of // must be multiple of
// 4 (LineLayer/PolygonLayer), // 4 (LineLayer/PolygonLayer),
// 6 (TexLineLayer) // 24 (TexLineLayer - one block, i.e. two segments)
// 24 (TextureLayer) // 24 (TextureLayer)
public static final int SIZE = 360; public static final int SIZE = 360;
} }

View File

@ -20,6 +20,7 @@ import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.GLRenderer.Matrices; import org.oscim.renderer.GLRenderer.Matrices;
import org.oscim.renderer.GLState; import org.oscim.renderer.GLState;
import org.oscim.renderer.LineRenderer; import org.oscim.renderer.LineRenderer;
import org.oscim.renderer.LineTexRenderer;
import org.oscim.renderer.PolygonRenderer; import org.oscim.renderer.PolygonRenderer;
import org.oscim.renderer.TextureRenderer; import org.oscim.renderer.TextureRenderer;
import org.oscim.renderer.layer.Layer; import org.oscim.renderer.layer.Layer;
@ -34,8 +35,6 @@ public abstract class BasicOverlay extends RenderOverlay {
public final Layers layers; public final Layers layers;
public BufferObject vbo;
protected float[] mvp = new float[16]; protected float[] mvp = new float[16];
public BasicOverlay(MapView mapView) { public BasicOverlay(MapView mapView) {
@ -51,17 +50,23 @@ public abstract class BasicOverlay extends RenderOverlay {
float div = FastMath.pow(mMapPosition.zoomLevel - pos.zoomLevel); 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); GLState.test(false, false);
if (layers.layers != null) { if (layers.baseLayers != null) {
setMatrix(pos, m, true); setMatrix(pos, m, true);
for (Layer l = layers.layers; l != null;) { for (Layer l = layers.baseLayers; l != null;) {
if (l.type == Layer.POLYGON) { switch (l.type) {
case Layer.POLYGON:
l = PolygonRenderer.draw(pos, l, m.mvp, true, false); l = PolygonRenderer.draw(pos, l, m.mvp, true, false);
} else { break;
l = LineRenderer.draw(pos, l, m.mvp, div, 0, layers.lineOffset); 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() { public void compile() {
int newSize = layers.getSize(); int newSize = layers.getSize();
if (newSize == 0) { if (newSize == 0) {
BufferObject.release(vbo); BufferObject.release(layers.vbo);
vbo = null; layers.vbo = null;
isReady = false; isReady = false;
return; return;
} }
if (vbo == null) { if (layers.vbo == null) {
vbo = BufferObject.get(0); layers.vbo = BufferObject.get(0);
if (vbo == null) if (layers.vbo == null)
return; return;
} }
if (newSize > 0) { if (newSize > 0) {
if (GLRenderer.uploadLayers(layers, vbo, newSize, true)) if (GLRenderer.uploadLayers(layers, newSize, true))
isReady = true; isReady = true;
} }
} }

View File

@ -40,85 +40,51 @@ public class TestLineOverlay extends RenderOverlay {
super(mapView); super(mapView);
} }
// Interleave two quads to be able to use vertices // Interleave two segment quads in one block to be able to use
// twice. pos0 and pos1 use the same vertex array // vertices twice. pos0 and pos1 use the same vertex array where
// where pos1 is off-setted by one vertex. The // pos1 is off-setted by one vertex. The vertex shader will use
// vertex shader will use pos0 when the vertexId // pos0 when the vertexId is even, pos1 when the Id is odd.
// is even, pos1 when the Id is odd.
// //
// As there is no gl_VertexId in gles 2.0 an // As there is no gl_VertexId in gles 2.0 an additional 'flip'
// additional 'flip' array is used. // array is used. Depending on 'flip' extrusion is inverted.
// Depending on 'flip' extrusion is inverted.
// //
// Indices and flip buffers can be static. // Indices and flip buffers can be static.
// //
// First pass: using even vertex array positions // First pass: using even vertex array positions
// (used vertices are in braces) // (used vertices are in braces)
// vertex id 0, 1, 2, 3 // vertex id 0 1 2 3 4 5 6 7
// pos0 - (0) 1 (2) 3 - // pos0 - (0) 1 (2) 3 (4) 5 (6) 7 -
// pos1 - (0) 1 (2) 3 - // pos1 - (0) 1 (2) 3 (4) 5 (6) 7 -
// flip 0 1 0 1 // flip 0 1 0 1 0 1 0 1
// //
// Second pass: using odd vertex array positions // Second pass: using odd vertex array positions
// vertex id 0, 1, 2, 3 // vertex id 0 1 2 3 4 5 6 7
// pos0 - 0 (1) 2 (3) - // pos0 - 0 (1) 2 (3) 4 (5) 6 (7) -
// pos1 - 0 (1) 2 (3) - // pos1 - 0 (1) 2 (3) 4 (5) 6 (7) -
// flip 0 1 0 1 // flip 0 1 0 1 0 1 0 1
// //
// Vertex layout: // Vertex layout:
// x/y pos[16][16], dir_x[8]|dir_y[8], start[4]|length[12] // [2 short] position,
// - 'direction' precision 1/16, maximum line width is 2*16 // [2 short] extrusion,
// - texture 'start' prescision 1 // [1 short] line length
// -> max tex width is 32 // [1 short] unused
// - 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,
// //
// -800, 200, 127, 0, // indices: (two indice blocks)
// 0, 200, 127, 0, // 0, 1, 2,
// 0, 200, 127, 1, // 2, 1, 3,
// 800, 200, 127, 1, // 4, 5, 6,
// // 6, 5, 7,
// -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;
private static int testProgram; private static int testProgram;
private static int htestVertexPosition0; private static int htestVertexPosition0;
private static int htestVertexPosition1; private static int htestVertexPosition1;
private static int htestVertexLength0;
private static int htestVertexLength1;
private static int htestVertexFlip; private static int htestVertexFlip;
private static int htestMatrix; private static int htestMatrix;
private static int htestColor; private static int htestTexColor;
private static int htestBgColor;
private static int htestScale;
private boolean initialized = false; private boolean initialized = false;
@ -126,38 +92,52 @@ public class TestLineOverlay extends RenderOverlay {
+ "precision mediump float;" + "precision mediump float;"
+ "uniform mat4 u_mvp;" + "uniform mat4 u_mvp;"
+ "uniform vec4 u_color;" + "uniform vec4 u_color;"
+ "uniform float u_scale;"
+ "attribute vec4 a_pos0;" + "attribute vec4 a_pos0;"
+ "attribute vec4 a_pos1;" + "attribute vec4 a_pos1;"
+ "attribute vec2 a_len0;"
+ "attribute vec2 a_len1;"
+ "attribute float a_flip;" + "attribute float a_flip;"
+ "varying vec4 color;" + "varying vec2 v_st;"
+ "const float ff = 256.0;"
+ "const float ffff = 65536.0;"
+ "void main() {" + "void main() {"
+ " float div = (8.0 * 16.0) / max(ceil(log(u_scale)),1.0);"
+ " if (a_flip == 0.0){" + " if (a_flip == 0.0){"
// extract 8 bit direction vector
+ " vec2 dir = a_pos0.zw/16.0;" + " vec2 dir = a_pos0.zw/16.0;"
+ " gl_Position = u_mvp * vec4(a_pos0.xy + dir, 0.0, 1.0);" + " gl_Position = u_mvp * vec4(a_pos0.xy + dir / u_scale, 0.0, 1.0);"
+ " color = vec4(dir/255.0 + 0.5, 1.0,1.0);" + " v_st = vec2(a_len0.x/div, 1.0);"
+ " }else {" + " }else {"
+ " vec2 dir = a_pos1.zw/16.0;" + " vec2 dir = a_pos1.zw/16.0;"
+ " gl_Position = u_mvp * vec4(a_pos1.xy - dir, 0.0, 1.0);" + " gl_Position = u_mvp * vec4(a_pos1.xy - dir / u_scale, 0.0, 1.0);"
+ " color = vec4(dir/255.0 + 0.5, 1.0,1.0);" + " v_st = vec2(a_len1.x/div, -1.0);"
+ "}}"; + " }"
+ "}";
final static String testFragmentShader = "" final static String testFragmentShader = ""
+ "precision mediump float;" + "precision mediump float;"
+ "varying vec4 color;" + "uniform sampler2D tex;"
+ " uniform vec4 u_color;"
+ " uniform vec4 u_bgcolor;"
+ "varying vec2 v_st;"
+ "void main() {" + "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 mIndicesBufferID;
private int mVertexBufferID; private int mVertexBufferID;
private int mVertexFlipID; private int mVertexFlipID;
private int mNumVertices; //private int mNumVertices;
private int mNumIndices; private int mNumIndices;
private int mTexID;
@Override @Override
public synchronized void update(MapPosition curPos, boolean positionChanged, public synchronized void update(MapPosition curPos, boolean positionChanged,
boolean tilesChanged) { boolean tilesChanged) {
@ -174,9 +154,14 @@ public class TestLineOverlay extends RenderOverlay {
return; return;
} }
htestMatrix = GLES20.glGetUniformLocation(testProgram, "u_mvp"); 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"); htestVertexPosition0 = GLES20.glGetAttribLocation(testProgram, "a_pos0");
htestVertexPosition1 = GLES20.glGetAttribLocation(testProgram, "a_pos1"); htestVertexPosition1 = GLES20.glGetAttribLocation(testProgram, "a_pos1");
htestVertexLength0 = GLES20.glGetAttribLocation(testProgram, "a_len0");
htestVertexLength1 = GLES20.glGetAttribLocation(testProgram, "a_len1");
htestVertexFlip = GLES20.glGetAttribLocation(testProgram, "a_flip"); htestVertexFlip = GLES20.glGetAttribLocation(testProgram, "a_flip");
int[] mVboIds = new int[3]; int[] mVboIds = new int[3];
@ -186,34 +171,37 @@ public class TestLineOverlay extends RenderOverlay {
mVertexFlipID = mVboIds[2]; mVertexFlipID = mVboIds[2];
float points[] = { float points[] = {
800, 0, -800, -800,
0, 0, 800, -800,
//-400, 100, 800, 800,
//-600, 200, -800, 800,
//-800, 100, -800, -800,
}; };
// float[] points = new float[12 * 2]; // float[] points = new float[12 * 2];
// for (int i = 0; i < 24; i += 2) { // for (int i = 0; i < 24; i += 2) {
// points[i + 0] = (float) Math.sin(-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) * 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 x = points[0];
float y = points[1]; float y = points[1];
float scale = 127; float scale = 255;
boolean even = true; boolean even = true;
float len = 0;
for (int i = 2; i < points.length;) { for (int i = 2; i < points.length; i += 2) {
float nx = points[i++]; float nx = points[i + 0];
float ny = points[i++]; float ny = points[i + 1];
// Calculate triangle corners for the given width // Calculate triangle corners for the given width
float vx = nx - x; float vx = nx - x;
@ -225,7 +213,7 @@ public class TestLineOverlay extends RenderOverlay {
vx /= a; vx /= a;
vy /= a; vy /= a;
// perpendicular // perpendicular to line segment
float ux = -vy; float ux = -vy;
float uy = vx; float uy = vx;
@ -236,33 +224,41 @@ public class TestLineOverlay extends RenderOverlay {
vertices[opos + 1] = (short) y; vertices[opos + 1] = (short) y;
vertices[opos + 2] = dx; vertices[opos + 2] = dx;
vertices[opos + 3] = dy; vertices[opos + 3] = dy;
vertices[opos + 4] = (short) len;
vertices[opos + 5] = 0;
vertices[opos + 8] = (short) nx; len += a;
vertices[opos + 9] = (short) ny; vertices[opos + 12] = (short) nx;
vertices[opos + 10] = dx; vertices[opos + 13] = (short) ny;
vertices[opos + 11] = dy; vertices[opos + 14] = dx;
vertices[opos + 15] = dy;
vertices[opos + 16] = (short) len;
vertices[opos + 17] = 0;
x = nx; x = nx;
y = ny; y = ny;
if (even) { if (even) {
opos += 4; // go to second segment
opos += 6;
even = false; even = false;
} else { } else {
// go to next block
even = true; 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++) for (int i = 0; i < flip.length; i++)
flip[i] = (byte) (i % 2); flip[i] = (byte) (i % 2);
short j = 0; short j = 0;
mNumIndices = ((points.length) >> 2) * 6; mNumIndices = ((points.length) >> 2) * 6;
indices = new short[mNumIndices]; short[] indices = new short[mNumIndices];
for (int i = 0; i < mNumIndices; i += 6, j += 4) { for (int i = 0; i < mNumIndices; i += 6, j += 4) {
indices[i + 0] = (short) (j + 0); indices[i + 0] = (short) (j + 0);
indices[i + 1] = (short) (j + 1); indices[i + 1] = (short) (j + 1);
@ -273,7 +269,7 @@ public class TestLineOverlay extends RenderOverlay {
indices[i + 5] = (short) (j + 3); indices[i + 5] = (short) (j + 3);
} }
ByteBuffer buf = ByteBuffer.allocateDirect(128 * 4) ByteBuffer buf = ByteBuffer.allocateDirect(numVertices * 6 * 2)
.order(ByteOrder.nativeOrder()); .order(ByteOrder.nativeOrder());
ShortBuffer sbuf = buf.asShortBuffer(); ShortBuffer sbuf = buf.asShortBuffer();
@ -285,7 +281,6 @@ public class TestLineOverlay extends RenderOverlay {
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
sbuf.clear(); sbuf.clear();
//sbuf.put(box);
sbuf.put(vertices); sbuf.put(vertices);
sbuf.flip(); sbuf.flip();
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBufferID); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBufferID);
@ -300,12 +295,23 @@ public class TestLineOverlay extends RenderOverlay {
GLES20.GL_STATIC_DRAW); GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); 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); mMapView.getMapViewPosition().getMapPosition(mMapPosition);
// tell GLRenderer to call 'render' // tell GLRenderer to call 'render'
isReady = true; isReady = true;
} }
private final static int STRIDE = 12;
private final static int LEN_OFFSET = 8;
@Override @Override
public synchronized void render(MapPosition pos, Matrices m) { public synchronized void render(MapPosition pos, Matrices m) {
@ -318,42 +324,79 @@ public class TestLineOverlay extends RenderOverlay {
GLState.enableVertexArrays(-1, -1); GLState.enableVertexArrays(-1, -1);
GLES20.glEnableVertexAttribArray(htestVertexPosition0); GLES20.glEnableVertexAttribArray(htestVertexPosition0);
GLES20.glEnableVertexAttribArray(htestVertexPosition1); GLES20.glEnableVertexAttribArray(htestVertexPosition1);
GlUtils.checkGlError("-4");
GLES20.glEnableVertexAttribArray(htestVertexLength0);
GlUtils.checkGlError("-3");
GLES20.glEnableVertexAttribArray(htestVertexLength1);
GlUtils.checkGlError("-2");
GLES20.glEnableVertexAttribArray(htestVertexFlip); GLES20.glEnableVertexAttribArray(htestVertexFlip);
GLES20.glUniformMatrix4fv(htestMatrix, 1, false, m.mvp, 0); 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.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); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBufferID);
GlUtils.checkGlError("1");
// first pass
GLES20.glVertexAttribPointer(htestVertexPosition0, 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, 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.glVertexAttribPointer(htestVertexLength1,
GLES20.glDrawElements(GLES20.GL_TRIANGLES, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0); 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, 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, 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.glVertexAttribPointer(htestVertexLength1,
GLES20.glDrawElements(GLES20.GL_TRIANGLES, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0); 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.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
GLES20.glDisableVertexAttribArray(htestVertexPosition0); GLES20.glDisableVertexAttribArray(htestVertexPosition0);
GLES20.glDisableVertexAttribArray(htestVertexPosition1); GLES20.glDisableVertexAttribArray(htestVertexPosition1);
GLES20.glDisableVertexAttribArray(htestVertexLength0);
GLES20.glDisableVertexAttribArray(htestVertexLength1);
GLES20.glDisableVertexAttribArray(htestVertexFlip); GLES20.glDisableVertexAttribArray(htestVertexFlip);
GlUtils.checkGlError("..."); GlUtils.checkGlError("...");
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
} }
@Override @Override

View File

@ -14,13 +14,10 @@
*/ */
package org.oscim.renderer.overlays; package org.oscim.renderer.overlays;
import java.io.IOException;
import org.oscim.core.MapPosition; import org.oscim.core.MapPosition;
import org.oscim.renderer.layer.SymbolItem; import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.SymbolLayer; import org.oscim.renderer.layer.LineTexLayer;
import org.oscim.renderer.layer.TextItem; import org.oscim.renderer.layer.TextItem;
import org.oscim.theme.renderinstruction.BitmapUtils;
import org.oscim.view.MapView; import org.oscim.view.MapView;
public class TestOverlay extends BasicOverlay { public class TestOverlay extends BasicOverlay {
@ -34,12 +31,33 @@ public class TestOverlay extends BasicOverlay {
public TestOverlay(MapView mapView) { public TestOverlay(MapView mapView) {
super(mapView); super(mapView);
// draw a rectangle
//LineLayer ll = (LineLayer) layers.getLayer(1, Layer.LINE); //LineLayer ll = (LineLayer) layers.getLayer(1, Layer.LINE);
//ll.line = new Line(Color.BLUE, 1.0f, Cap.BUTT); //ll.line = new Line(Color.BLUE, 1.0f, Cap.BUTT);
//ll.width = 2; //ll.width = 2;
// float[] points = { -100, -100, 100, -100, 100, 100, -100, 100, -100, -100 }; float[] points = {
-100, -100,
100, -100,
100, 100,
-100, 100,
-100, -100
};
//short[] index = { (short) points.length }; //short[] index = { (short) points.length };
// ll.addLine(points, index, false); //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); // PolygonLayer pl = (PolygonLayer) layers.getLayer(0, Layer.POLYGON);
@ -54,33 +72,33 @@ public class TestOverlay extends BasicOverlay {
// short[] pindex = { (short) ppoints.length }; // short[] pindex = { (short) ppoints.length };
// pl.addPolygon(ppoints, pindex); // pl.addPolygon(ppoints, pindex);
SymbolLayer sl = new SymbolLayer(); //SymbolLayer sl = new SymbolLayer();
SymbolItem it = new SymbolItem(); //SymbolItem it = new SymbolItem();
//
it.x = 0; //it.x = 0;
it.y = 0; //it.y = 0;
// billboard always faces camera //// billboard always faces camera
it.billboard = true; //it.billboard = true;
//
try { //try {
it.bitmap = BitmapUtils.createBitmap("file:/sdcard/cheshire.png"); // it.bitmap = BitmapUtils.createBitmap("file:/sdcard/cheshire.png");
} catch (IOException e) { //} catch (IOException e) {
// TODO Auto-generated catch block // // TODO Auto-generated catch block
e.printStackTrace(); // e.printStackTrace();
} //}
sl.addSymbol(it); //sl.addSymbol(it);
//
SymbolItem it2 = new SymbolItem(); //SymbolItem it2 = new SymbolItem();
it2.bitmap = it.bitmap; //it2.bitmap = it.bitmap;
it2.x = 0; //it2.x = 0;
it2.y = 0; //it2.y = 0;
// billboard always faces camera //// billboard always faces camera
it2.billboard = false; //it2.billboard = false;
//
sl.addSymbol(it2); //sl.addSymbol(it2);
sl.fixed = false; //sl.fixed = false;
//
layers.textureLayers = sl; //layers.textureLayers = sl;
// TextLayer tl = new TextLayer(); // TextLayer tl = new TextLayer();
// Text t = Text.createText(20, 2, Color.WHITE, Color.BLACK, false); // Text t = Text.createText(20, 2, Color.WHITE, Color.BLACK, false);
@ -106,7 +124,7 @@ public class TestOverlay extends BasicOverlay {
updateMapPosition(); updateMapPosition();
first = false; first = false;
((SymbolLayer) (layers.textureLayers)).prepare(); //((SymbolLayer) (layers.textureLayers)).prepare();
// pass layers to be uploaded and drawn to GL Thread // pass layers to be uploaded and drawn to GL Thread
// afterwards never modify 'layers' outside of this function! // afterwards never modify 'layers' outside of this function!

View File

@ -386,8 +386,8 @@ public class TextOverlay extends BasicOverlay {
layers.clear(); layers.clear();
if (mDebugLayer != null) { if (mDebugLayer != null) {
layers.layers = mDebugLayer.layers; layers.baseLayers = mDebugLayer.baseLayers;
mDebugLayer.layers = null; mDebugLayer.baseLayers = null;
} }
// set new TextLayer to be uploaded and used // set new TextLayer to be uploaded and used

View File

@ -627,7 +627,7 @@ public class TextOverlayExp extends BasicOverlay {
layers.clear(); layers.clear();
if (mDebugLayer != null) { if (mDebugLayer != null) {
layers.layers = mDebugLayer.layers; layers.baseLayers = mDebugLayer.baseLayers;
mDebugLayer = null; mDebugLayer = null;
} }
@ -665,12 +665,12 @@ public class TextOverlayExp extends BasicOverlay {
return; return;
} }
if (vbo == null) { if (layers.vbo == null) {
vbo = BufferObject.get(0); layers.vbo = BufferObject.get(0);
} }
if (newSize > 0) { if (newSize > 0) {
if (GLRenderer.uploadLayers(layers, vbo, newSize, true)) if (GLRenderer.uploadLayers(layers, newSize, true))
isReady = true; isReady = true;
} }
} }
@ -679,19 +679,19 @@ public class TextOverlayExp extends BasicOverlay {
public synchronized void render(MapPosition pos, Matrices m) { public synchronized void render(MapPosition pos, Matrices m) {
float div = FastMath.pow(mMapPosition.zoomLevel - pos.zoomLevel); 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); GLState.test(false, false);
if (layers.layers != null) { if (layers.baseLayers != null) {
setMatrix(pos, m, true); setMatrix(pos, m, true);
//Matrix.multiplyMM(m.mvp, 0, m.proj, 0, m.mvp,0); //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) { if (l.type == Layer.POLYGON) {
l = PolygonRenderer.draw(pos, l, m.mvp, true, false); l = PolygonRenderer.draw(pos, l, m.mvp, true, false);
} else { } else {
float scale = pos.scale * div; 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);
} }
} }
} }

View File

@ -229,35 +229,13 @@ public final class Line extends RenderInstruction {
this.fade = fade; this.fade = fade;
this.stipple = stipple; this.stipple = stipple;
this.min = min; 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, private Line(Line line, String style, String src, int stroke, float strokeWidth,
int stipple, Cap strokeLinecap, int level, boolean fixed, int stipple, Cap strokeLinecap, int level, boolean fixed,
int fade, float blur, boolean isOutline, float min) { int fade, float blur, boolean isOutline, float min) {

View File

@ -54,6 +54,13 @@ public class FastMath {
return (pow > 0 ? (1 << pow) : (1.0f / (1 << -pow))); 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){ public static float abs(float value){
return value < 0 ? -value : value; return value < 0 ? -value : value;

View File

@ -30,10 +30,25 @@ import android.util.Log;
public class GlUtils { public class GlUtils {
private static String TAG = "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 * @param bitmap
* ... * ...
* @return gl identifier * @return textureId
*/ */
public static int loadTextures(Bitmap bitmap) { public static int loadTextures(Bitmap bitmap) {
@ -44,21 +59,8 @@ public class GlUtils {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, setTextureParameter(GLES20.GL_LINEAR, GLES20.GL_LINEAR,
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_CLAMP_TO_EDGE, GLES20.GL_CLAMP_TO_EDGE);
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);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); 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, 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]; int[] textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0); GLES20.glGenTextures(1, textureIds, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, setTextureParameter(min_filter, mag_filter, wrap_s, wrap_t);
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
ByteBuffer buf = ByteBuffer.allocateDirect(width * height).order(ByteOrder.nativeOrder()); ByteBuffer buf = ByteBuffer.allocateDirect(width * height).order(ByteOrder.nativeOrder());
buf.put(pixel); buf.put(pixel);
@ -96,6 +87,37 @@ public class GlUtils {
return textureIds[0]; 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 * @param shaderType
* shader type * shader type