/* * 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.overlays; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.ShortBuffer; import org.oscim.core.MapPosition; import org.oscim.core.Tile; import org.oscim.renderer.GLRenderer; import org.oscim.renderer.GLRenderer.Matrices; import org.oscim.renderer.GLState; import org.oscim.utils.FastMath; import org.oscim.utils.GlUtils; import org.oscim.view.MapView; import android.opengl.GLES20; import android.opengl.Matrix; import android.util.Log; /** * @author Hannes Janetzek */ public class TestLineOverlay extends RenderOverlay { public TestLineOverlay(MapView mapView) { super(mapView); } // 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. // // 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 - (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 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: // [2 short] position, // [2 short] extrusion, // [1 short] line length // [1 short] unused // // 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 htestTexColor; private static int htestBgColor; private static int htestScale; private boolean initialized = false; final static String testVertexShader = "" + "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 testFragmentShader = "" + "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);" + " 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 mNumIndices; private int mTexID; @Override public synchronized void update(MapPosition curPos, boolean positionChanged, boolean tilesChanged) { if (initialized) return; initialized = true; // Set up the program for rendering tests testProgram = GlUtils.createProgram(testVertexShader, testFragmentShader); if (testProgram == 0) { Log.e("...", "Could not create test program."); return; } htestMatrix = GLES20.glGetUniformLocation(testProgram, "u_mvp"); 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]; GLES20.glGenBuffers(3, mVboIds, 0); mIndicesBufferID = mVboIds[0]; mVertexBufferID = mVboIds[1]; mVertexFlipID = mVboIds[2]; float points[] = { -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) * 8*400; // points[i + 1] = (float) Math.cos(-i / 11f * Math.PI) * 8*400; // } boolean oddSegments = points.length % 4 == 0; int numVertices = points.length + (oddSegments ? 2 : 0); short[] vertices = new short[numVertices * 6]; int opos = 6; float x = points[0]; float y = points[1]; float scale = 255; boolean even = true; float len = 0; 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; 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 * scale); short dy = (short) (uy * scale); vertices[opos + 0] = (short) x; vertices[opos + 1] = (short) y; vertices[opos + 2] = dx; vertices[opos + 3] = dy; vertices[opos + 4] = (short) len; vertices[opos + 5] = 0; 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) { // go to second segment opos += 6; even = false; } else { // go to next block even = true; opos += 18; } } // 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; 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); 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(numVertices * 6 * 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); sbuf.clear(); sbuf.put(vertices); sbuf.flip(); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBufferID); GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertices.length * 2, sbuf, GLES20.GL_STATIC_DRAW); 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); 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) { setMatrix(pos, m); //Matrix.multiplyMM(mv, 0, proj, 0, mv, 0); GLState.useProgram(testProgram); GLES20.glDisable(GLES20.GL_CULL_FACE); GLState.test(false, false); 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.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); GlUtils.checkGlError("0"); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBufferID); GlUtils.checkGlError("1"); // first pass GLES20.glVertexAttribPointer(htestVertexPosition0, 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, STRIDE, 0); GlUtils.checkGlError("4"); 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, STRIDE, 2 * STRIDE); GLES20.glVertexAttribPointer(htestVertexLength0, 2, GLES20.GL_SHORT, false, STRIDE, 2 * STRIDE + LEN_OFFSET); GLES20.glVertexAttribPointer(htestVertexPosition1, 4, GLES20.GL_SHORT, false, STRIDE, STRIDE); 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 protected void setMatrix(MapPosition curPos, Matrices m) { MapPosition oPos = mMapPosition; byte z = oPos.zoomLevel; float div = FastMath.pow(z - curPos.zoomLevel); float x = (float) (oPos.x - curPos.x * div); float y = (float) (oPos.y - curPos.y * div); // flip around date-line float max = (Tile.TILE_SIZE << z); if (x < -max / 2) x = max + x; else if (x > max / 2) x = x - max; float scale = curPos.scale / div; Matrix.setIdentityM(m.mvp, 0); // translate relative to map center m.mvp[12] = x * scale; m.mvp[13] = y * scale; // scale to current tile world coordinates scale = (curPos.scale / oPos.scale) / div; scale /= GLRenderer.COORD_SCALE; m.mvp[0] = scale; m.mvp[5] = scale; m.mvp[10] = 1; //scale; // 1000f; Matrix.multiplyMM(m.mvp, 0, m.viewproj, 0, m.mvp, 0); } @Override public void compile() { } }