- use Stencil instead of Depth for clipping test

to avoid drawing everything with polygonOffset
- add GLState.useProgram
This commit is contained in:
Hannes Janetzek 2013-01-30 04:05:44 +01:00
parent 5f03a33492
commit be748138ac
9 changed files with 297 additions and 167 deletions

View File

@ -16,7 +16,6 @@ package org.oscim.renderer;
import static android.opengl.GLES20.GL_ARRAY_BUFFER;
import static android.opengl.GLES20.GL_BLEND;
import static android.opengl.GLES20.GL_POLYGON_OFFSET_FILL;
import static org.oscim.generator.JobTile.STATE_READY;
import org.oscim.core.MapPosition;
@ -31,7 +30,7 @@ import android.opengl.Matrix;
* @author Hannes Janetzek
*/
public class BaseMap {
private final static String TAG = BaseMap.class.getName();
//private final static String TAG = BaseMap.class.getName();
// used to not draw a tile twice per frame.
private static int mDrawSerial = 0;
@ -52,10 +51,10 @@ public class BaseMap {
static void draw(MapTile[] tiles, int tileCnt, MapPosition pos) {
//long start = SystemClock.uptimeMillis();
Matrix.multiplyMM(mVPMatrix, 0, mfProjMatrix, 0, pos.viewMatrix, 0);
mDrawCnt = 0;
GLES20.glEnable(GL_POLYGON_OFFSET_FILL);
GLES20.glDepthFunc(GLES20.GL_LESS);
LineRenderer.beginLines();
for (int i = 0; i < tileCnt; i++) {
MapTile t = tiles[i];
@ -63,9 +62,8 @@ public class BaseMap {
drawTile(t, pos);
}
// proxies are clipped to the region where nothing was drawn to depth
// buffer.
// TODO draw all parent before grandparent
// proxies are clipped to the region where nothing was drawn to depth buffer.
// draw child or parent proxies.
// TODO draw proxies for placeholder...
for (int i = 0; i < tileCnt; i++) {
MapTile t = tiles[i];
@ -73,6 +71,7 @@ public class BaseMap {
drawProxyTile(t, pos, true);
}
// draw grandparents
for (int i = 0; i < tileCnt; i++) {
MapTile t = tiles[i];
if (t.isVisible && (t.state != STATE_READY) && (t.holder == null))
@ -81,8 +80,6 @@ public class BaseMap {
LineRenderer.endLines();
GLES20.glDisable(GL_POLYGON_OFFSET_FILL);
//GLES20.glFinish();
//long end = SystemClock.uptimeMillis();
//Log.d(TAG, "base took " + (end - start));
mDrawSerial++;
@ -103,9 +100,10 @@ public class BaseMap {
if (t.holder != null)
t = t.holder;
if (t.layers == null || t.vbo == null)
if (t.layers == null || t.vbo == null) {
//Log.d(TAG, "missing data " + (t.layers == null) + " " + (t.vbo == null));
return;
}
// set Model matrix for tile
float div = FastMath.pow(tile.zoomLevel - pos.zoomLevel);
float x = (float) (tile.pixelX - pos.x * div);
@ -117,8 +115,7 @@ public class BaseMap {
Matrix.multiplyMM(mvp, 0, mVPMatrix, 0, mvp, 0);
// set depth offset (used for clipping to tile boundaries)
//GLES20.glPolygonOffset(-1, -GLRenderer.depthOffset(t));
GLES20.glPolygonOffset(-1, (mDrawCnt++));
GLES20.glPolygonOffset(1, mDrawCnt++);
GLES20.glBindBuffer(GL_ARRAY_BUFFER, t.vbo.id);
@ -140,8 +137,6 @@ public class BaseMap {
PolygonRenderer.draw(pos, null, mvp, true, true);
clipped = true;
}
// clip lines to quad in depth buffer
GLState.test(true, false);
GLES20.glEnable(GL_BLEND);
l = LineRenderer.draw(pos, l, mvp, div, simpleShader,
@ -149,6 +144,7 @@ public class BaseMap {
break;
}
}
PolygonRenderer.drawOver(mvp);
}
private static int drawProxyChild(MapTile tile, MapPosition pos) {
@ -179,8 +175,9 @@ public class BaseMap {
// draw parent proxy
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
MapTile t = tile.rel.parent.tile;
if (t.state == STATE_READY)
if (t.state == STATE_READY) {
drawTile(t, pos);
}
}
} else if ((tile.proxies & MapTile.PROXY_GRAMPA) != 0) {
// check if parent was already drawn
@ -197,14 +194,14 @@ public class BaseMap {
} else {
// prefer drawing parent
if (parent) {
MapTile t = tile.rel.parent.tile;
if (t != null && t.state == STATE_READY) {
drawTile(t, pos);
return;
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
MapTile t = tile.rel.parent.tile;
if (t != null && t.state == STATE_READY) {
drawTile(t, pos);
return;
}
}
drawProxyChild(tile, pos);
} else if ((tile.proxies & MapTile.PROXY_GRAMPA) != 0) {

View File

@ -27,6 +27,7 @@ public class GLState {
private static boolean blend = false;
private static boolean depth = false;
private static boolean stencil = false;
private static int shader;
public static void init() {
vertexArray[0] = false;
@ -34,11 +35,19 @@ public class GLState {
blend = false;
depth = false;
stencil = false;
shader = -1;
GLES20.glDisable(GLES20.GL_STENCIL_TEST);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
}
public static void useProgram(int shaderProgram) {
if (shaderProgram != shader) {
GLES20.glUseProgram(shaderProgram);
shader = shaderProgram;
}
}
// TODO
public static void blend(boolean enable) {
if (blend == enable)
return;

View File

@ -20,9 +20,7 @@ import static android.opengl.GLES20.glDrawArrays;
import static android.opengl.GLES20.glGetAttribLocation;
import static android.opengl.GLES20.glGetUniformLocation;
import static android.opengl.GLES20.glUniform1f;
import static android.opengl.GLES20.glUniform1i;
import static android.opengl.GLES20.glUniformMatrix4fv;
import static android.opengl.GLES20.glUseProgram;
import static android.opengl.GLES20.glVertexAttribPointer;
import org.oscim.core.MapPosition;
@ -119,7 +117,7 @@ public final class LineRenderer {
if (layer == null)
return null;
glUseProgram(lineProgram[mode]);
GLState.useProgram(lineProgram[mode]);
int uLineScale = hLineScale[mode];
int uLineMode = hLineMode[mode];
@ -153,7 +151,7 @@ public final class LineRenderer {
glUniform1f(uLineScale, pixel);
int lineMode = 0;
glUniform1i(uLineMode, lineMode);
glUniform1f(uLineMode, lineMode);
float blurScale = pixel;
boolean blur = false;
@ -209,11 +207,11 @@ public final class LineRenderer {
if (o.roundCap) {
if (lineMode != 1) {
lineMode = 1;
glUniform1i(uLineMode, lineMode);
glUniform1f(uLineMode, lineMode);
}
} else if (lineMode != 0) {
lineMode = 0;
glUniform1i(uLineMode, lineMode);
glUniform1f(uLineMode, lineMode);
}
glDrawArrays(GL_TRIANGLE_STRIP, o.offset, o.verticesCnt);
}
@ -244,11 +242,11 @@ public final class LineRenderer {
if (ll.roundCap) {
if (lineMode != 1) {
lineMode = 1;
glUniform1i(uLineMode, lineMode);
glUniform1f(uLineMode, lineMode);
}
} else if (lineMode != 0) {
lineMode = 0;
glUniform1i(uLineMode, lineMode);
glUniform1f(uLineMode, lineMode);
}
glDrawArrays(GL_TRIANGLE_STRIP, l.offset, l.verticesCnt);
@ -263,8 +261,9 @@ public final class LineRenderer {
+ "uniform mat4 u_mvp;"
+ "uniform float u_width;"
+ "attribute vec4 a_pos;"
+ "uniform int u_mode;"
+ "uniform float u_mode;"
+ "varying vec2 v_st;"
+ "varying vec2 v_mode;"
+ "const float dscale = 8.0/2048.0;"
+ "void main() {"
// scale extrusion to u_width pixel
@ -274,22 +273,27 @@ 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 int u_mode;"
+ "uniform float u_width;"
+ "uniform float u_wscale;"
+ "uniform vec4 u_color;"
+ "varying vec2 v_st;"
+ "varying vec2 v_mode;"
+ "void main() {"
+ " float len;"
+ " if (u_mode == 0)"
+ " len = abs(v_st.s);"
+ " else"
+ " len = texture2D(tex, v_st).a;"
//+ " float len;"
// some say one should not use conditionals
// (FIXME 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);"
// 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);"
@ -300,7 +304,7 @@ public final class LineRenderer {
+ "#extension GL_OES_standard_derivatives : enable\n"
+ "precision mediump float;"
+ "uniform sampler2D tex;"
+ "uniform int u_mode;"
+ "uniform float u_mode;"
+ "uniform vec4 u_color;"
+ "uniform float u_width;"
+ "uniform float u_wscale;"
@ -308,7 +312,7 @@ public final class LineRenderer {
+ "void main() {"
+ " float len;"
+ " float fuzz;"
+ " if (u_mode == 0){"
+ " if (u_mode == 0.0){"
+ " len = abs(v_st.s);"
+ " fuzz = fwidth(v_st.s);"
+ " } else {"
@ -317,10 +321,12 @@ public final class LineRenderer {
+ " vec2 st_width = fwidth(v_st);"
+ " fuzz = max(st_width.s, st_width.t);"
+ " }"
// smoothstep is too sharp, guess one could increase extrusion with z..
// but this looks ok too:
+ " gl_FragColor = u_color * min(1.0, (1.0 - len) / (u_wscale + fuzz));"
//+ " gl_FragColor = u_color * smoothstep(0.0, fuzz + u_wscale, 1.0 - len);"
// smoothstep is too sharp, guess one could increase extrusion with z..
// this looks ok:
//+ " gl_FragColor = u_color * min(1.0, (1.0 - len) / (u_wscale + fuzz));"
// can be faster according to nvidia docs 'Optimize OpenGL ES 2.0 Performace'
+ " gl_FragColor = u_color * clamp((1.0 - len) / (u_wscale + fuzz), 0.0, 1.0);"
+ "}";
// private final static String lineVertexShader = ""

View File

@ -18,13 +18,11 @@ import static android.opengl.GLES20.GL_ALWAYS;
import static android.opengl.GLES20.GL_BLEND;
import static android.opengl.GLES20.GL_EQUAL;
import static android.opengl.GLES20.GL_INVERT;
import static android.opengl.GLES20.GL_LESS;
import static android.opengl.GLES20.GL_SHORT;
import static android.opengl.GLES20.GL_TRIANGLE_FAN;
import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
import static android.opengl.GLES20.GL_ZERO;
import static android.opengl.GLES20.glColorMask;
import static android.opengl.GLES20.glDepthFunc;
import static android.opengl.GLES20.glDepthMask;
import static android.opengl.GLES20.glDisable;
import static android.opengl.GLES20.glDrawArrays;
@ -36,7 +34,6 @@ import static android.opengl.GLES20.glStencilMask;
import static android.opengl.GLES20.glStencilOp;
import static android.opengl.GLES20.glUniform4fv;
import static android.opengl.GLES20.glUniformMatrix4fv;
import static android.opengl.GLES20.glUseProgram;
import static android.opengl.GLES20.glVertexAttribPointer;
import java.nio.ByteBuffer;
@ -51,7 +48,7 @@ import org.oscim.utils.GlUtils;
import android.opengl.GLES20;
public final class PolygonRenderer {
// private static final String TAG = "PolygonRenderer";
//private static final String TAG = PolygonRenderer.class.getName();
// private static final int NUM_VERTEX_SHORTS = 2;
private static final int POLYGON_VERTICES_DATA_POS_OFFSET = 0;
@ -93,7 +90,7 @@ public final class PolygonRenderer {
boolean blend = false;
/* draw to framebuffer */
glColorMask(true, true, true, true);
glColorMask(true, true, true, false);
/* do not modify stencil buffer */
glStencilMask(0);
@ -144,18 +141,10 @@ public final class PolygonRenderer {
glUniform4fv(hPolygonColor, 1, l.area.color, 0);
}
// if (alpha < 1) {
// if (!blend) {
// glEnable(GL_BLEND);
// blend = true;
// }
// } else if (blend) {
// glDisable(GL_BLEND);
// blend = false;
// }
/* set stencil buffer mask used to draw this layer */
glStencilFunc(GL_EQUAL, 0xff, 1 << c);
// set stencil buffer mask used to draw this layer
// also check that clip bit is 0 to avoid overdraw
// of other tiles
glStencilFunc(GL_EQUAL, 0x7f, 0x80 | 1 << c);
/* draw tile fill coordinates */
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
@ -195,7 +184,7 @@ public final class PolygonRenderer {
int zoom = pos.zoomLevel;
float scale = pos.scale;
glUseProgram(polygonProgram);
GLState.useProgram(polygonProgram);
GLState.enableVertexArrays(hPolygonVertexPosition, -1);
@ -204,82 +193,33 @@ public final class PolygonRenderer {
glUniformMatrix4fv(hPolygonMatrix, 1, false, matrix, 0);
// reset start when only two layer left in stencil buffer
// if (mCount > 5) {
// mCount = 0;
// mStart = 0;
// }
if (first) {
// reset start when only one layer left in stencil buffer
if (first || mCount > 5) {
mCount = 0;
mStart = 0;
} else {
mStart = mCount;
}
//GLState.test(drawClipped, true);
GLState.test(true, true);
GLState.test(false, true);
Layer l = layer;
for (; l != null && l.type == Layer.POLYGON; l = l.next) {
PolygonLayer pl = (PolygonLayer) l;
// fade out polygon layers (set in RederTheme)
// fade out polygon layers (set in RenderTheme)
if (pl.area.fade > 0 && pl.area.fade > zoom)
continue;
if (mCount == mStart) {
/* clear stencilbuffer (tile region) by drawing
* a quad with func 'always' and op 'zero' */
// disable drawing to framebuffer
glColorMask(false, false, false, false);
// if (!first) {
// // first run draw map bg, otherwise
// // disable drawing to framebuffer
// glColorMask(false, false, false, false);
// } else {
// glUniform4fv(hPolygonColor, 1, GLRenderer.mClearColor, 0);
// }
// never pass the test: always apply fail op
glStencilFunc(GL_ALWAYS, 0, 0xFF);
glStencilMask(0xFF);
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
/* draw clip-region into depth buffer:
* this is used for lines and polygons */
if (first && drawClipped) {
// write to depth buffer
glDepthMask(true);
// to prevent overdraw gl_less restricts the
// clip to the area where no other tile has drawn
glDepthFunc(GL_LESS);
}
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// if (first) {
// glColorMask(false, false, false, false);
// }
if (first && drawClipped) {
first = false;
// do not modify depth buffer anymore
glDepthMask(false);
// only draw to this tile
glDepthFunc(GL_EQUAL);
}
drawStencilRegion(drawClipped, first);
first = false;
// op for stencil method polygon drawing
glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, GL_INVERT);
}
// no need for depth test while drawing stencil
//GLState.test(false, true);
mFillPolys[mCount] = pl;
// set stencil mask to draw to
@ -287,43 +227,113 @@ public final class PolygonRenderer {
glDrawArrays(GL_TRIANGLE_FAN, l.offset, l.verticesCnt);
// draw up to 8 layers into stencil buffer
if (mCount == STENCIL_BITS) {
/* only draw where nothing was drawn yet */
if (drawClipped)
GLState.test(true, true);
// draw up to 7 layers into stencil buffer
if (mCount == STENCIL_BITS - 1) {
fillPolygons(zoom, scale);
mCount = 0;
mStart = 0;
}
}
if (mCount > 0) {
/* only draw where nothing was drawn yet */
if (drawClipped)
GLState.test(true, true);
if (mCount > 0)
fillPolygons(zoom, scale);
if (drawClipped) {
if (first) {
drawStencilRegion(drawClipped, first);
glStencilMask(0x00);
glColorMask(true, true, true, false);
}
// clip bit must be zero
glStencilFunc(GL_EQUAL, 0x00, 0x80);
}
if (drawClipped && first) {
GLState.test(true, false);
GLES20.glColorMask(false, false, false, false);
//glUniform4fv(hPolygonColor, 1, GLRenderer.mClearColor, 0);
GLES20.glDepthMask(true);
GLES20.glDepthFunc(GLES20.GL_LESS);
GLES20.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
GLES20.glDepthMask(false);
GLES20.glColorMask(true, true, true, true);
GLES20.glDepthFunc(GLES20.GL_EQUAL);
}
return l;
}
static void drawStencilRegion(boolean clip, boolean first) {
/* clear stencilbuffer (tile region) by drawing
* a quad with func 'always' and op 'zero' */
// disable drawing to framebuffer (will be re-enabled in fill)
glColorMask(false, false, false, false);
GLES20.glUniform4f(hPolygonColor, 0.7f, 0.7f, 0.7f, 1);
// write to all bits
glStencilMask(0xFF);
// zero out area to draw to
glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, GL_ZERO);
if (clip) {
if (first) {
// draw clip-region into depth and stencil buffer:
// this is used for tile line and polygon layers
// always pass stencil test:
glStencilFunc(GL_ALWAYS, 0x00, 0x00);
GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL);
// test depth. stencil passes always, just keep it enabled
GLState.test(true, true);
// write to depth buffer
glDepthMask(true);
}
else {
// use clip region from stencil buffer
glStencilFunc(GL_EQUAL, 0x00, 0x80);
}
} else {
// always pass stencil test:
glStencilFunc(GL_ALWAYS, 0x00, 0x00);
}
// draw a quad for the tile region
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
if (clip) {
if (first) {
// dont modify depth buffer
glDepthMask(false);
GLState.test(false, true);
GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL);
} else {
glStencilFunc(GL_ALWAYS, 0x00, 0x00);
}
}
}
static void drawOver(float[] matrix) {
GLState.useProgram(polygonProgram);
GLState.enableVertexArrays(hPolygonVertexPosition, -1);
glVertexAttribPointer(hPolygonVertexPosition, 2, GL_SHORT,
false, 0, POLYGON_VERTICES_DATA_POS_OFFSET);
glUniformMatrix4fv(hPolygonMatrix, 1, false, matrix, 0);
/* clear stencilbuffer (tile region) by drawing
* a quad with func 'always' and op 'zero' */
// disable drawing to framebuffer (will be re-enabled in fill)
glColorMask(false, false, false, false);
// always pass stencil test:
glStencilFunc(GL_ALWAYS, 0xFF, 0xFF);
// write to all bits
glStencilMask(0xFF);
// zero out area to draw to
glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, GLES20.GL_REPLACE);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glColorMask(true, true, true, true);
}
private static float[] debugFillColor = { 0.3f, 0.0f, 0.0f, 0.3f };
private static float[] debugFillColor2 = { .8f, .8f, .8f, .8f };
private static FloatBuffer mDebugFill;
@ -339,7 +349,7 @@ public final class PolygonRenderer {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
mDebugFill.position(0);
glUseProgram(polygonProgram);
GLState.useProgram(polygonProgram);
GLES20.glEnableVertexAttribArray(hPolygonVertexPosition);
glVertexAttribPointer(hPolygonVertexPosition, 2, GLES20.GL_FLOAT,

View File

@ -93,7 +93,7 @@ public final class TextureRenderer {
GLState.test(false, false);
GLES20.glEnable(GLES20.GL_BLEND);
// GlUtils.checkGlError("draw texture >");
GLES20.glUseProgram(mTextureProgram);
GLState.useProgram(mTextureProgram);
GLState.enableVertexArrays(hTextureTexCoord, hTextureVertex);

View File

@ -350,7 +350,7 @@ public class BuildingOverlay extends RenderOverlay {
setMatrix(pos, mv);
Matrix.multiplyMM(mv, 0, proj, 0, mv, 0);
GLES20.glUseProgram(buildingProgram);
GLState.useProgram(buildingProgram);
GLES20.glUniformMatrix4fv(hBuildingMatrix, 1, false, mv, 0);
GLES20.glUniform4f(hBuildingColor, 0.5f, 0.5f, 0.5f, 0.7f);

View File

@ -180,7 +180,7 @@ public class ExtrusionOverlay extends RenderOverlay {
int uExtMode = hExtrusionMode[shaderMode];
if (debug) {
GLES20.glUseProgram(extrusionProgram[shaderMode]);
GLState.useProgram(extrusionProgram[shaderMode]);
GLState.enableVertexArrays(uExtVertexPosition, uExtLightPosition);
GLES20.glUniform1i(uExtMode, 0);
@ -224,9 +224,11 @@ public class ExtrusionOverlay extends RenderOverlay {
GLState.test(true, false);
GLES20.glUseProgram(extrusionProgram[shaderMode]);
GLState.useProgram(extrusionProgram[shaderMode]);
GLState.enableVertexArrays(uExtVertexPosition, -1);
if (pos.scale < 2) {
// chances are high that one moves through a building
// with scale > 2 also draw back sides in this case.
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glCullFace(GLES20.GL_FRONT);
}

View File

@ -172,7 +172,7 @@ public class ModelOverlay extends RenderOverlay {
setMatrix(pos, mv);
Matrix.multiplyMM(mv, 0, proj, 0, mv, 0);
GLES20.glUseProgram(polygonProgram);
GLState.useProgram(polygonProgram);
GLState.enableVertexArrays(hPolygonVertexPosition, hPolygonLightPosition);

View File

@ -66,7 +66,7 @@ public class TestLineOverlay extends RenderOverlay {
//
// 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*32
// - 'direction' precision 1/16, maximum line width is 2*16
// - texture 'start' prescision 1
// -> max tex width is 32
// - segment 'length' prescision 1/4
@ -99,16 +99,18 @@ public class TestLineOverlay extends RenderOverlay {
0, 0, 0, 0,
};
private final short[] indices = {
private short[] indices = {
0, 1, 2,
2, 1, 3,
4, 5, 6,
6, 5, 7,
8, 9, 10,
10, 9, 11,
};
private byte[] flip = { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 };
private byte[] flip;
private static int testProgram;
private static int htestVertexPosition0;
@ -131,13 +133,14 @@ public class TestLineOverlay extends RenderOverlay {
+ "const float ffff = 65536.0;"
+ "void main() {"
+ " if (a_flip == 0.0){"
+ " vec2 dir = vec2((a_pos0.z / ffff), fract(a_pos0.z/ff))*64.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(0.0,1.0,a_pos0.w,1.0);"
+ " color = vec4(dir/255.0 + 0.5, 1.0,1.0);"
+ " }else {"
+ " vec2 dir = vec2((a_pos1.z / ffff), fract(a_pos1.z/ff))*64.0;"
+ " vec2 dir = a_pos1.zw/16.0;"
+ " gl_Position = u_mvp * vec4(a_pos1.xy - dir, 0.0, 1.0);"
+ " color = vec4(1.0,0.5,a_pos1.w,1.0);"
+ " color = vec4(dir/255.0 + 0.5, 1.0,1.0);"
+ "}}";
final static String testFragmentShader = ""
@ -151,6 +154,9 @@ public class TestLineOverlay extends RenderOverlay {
private int mVertexBufferID;
private int mVertexFlipID;
private int mNumVertices;
private int mNumIndices;
@Override
public synchronized void update(MapPosition curPos, boolean positionChanged,
boolean tilesChanged) {
@ -178,6 +184,93 @@ public class TestLineOverlay extends RenderOverlay {
mVertexBufferID = mVboIds[1];
mVertexFlipID = mVboIds[2];
float points[] = {
800, 0,
0, 0,
//-400, 100,
//-600, 200,
//-800, 100,
};
// 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;
// }
mNumVertices = (points.length - 2) * 2;
short[] vertices = new short[(mNumVertices + 2) * 4];
int opos = 4;
float x = points[0];
float y = points[1];
float scale = 127;
boolean even = true;
for (int i = 2; i < points.length;) {
float nx = points[i++];
float ny = points[i++];
// 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 = (vx / a);
vy = (vy / a);
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 + 8] = (short) nx;
vertices[opos + 9] = (short) ny;
vertices[opos + 10] = dx;
vertices[opos + 11] = dy;
x = nx;
y = ny;
if (even) {
opos += 4;
even = false;
} else {
even = true;
opos += 12;
}
}
flip = new byte[(points.length - 2)];
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];
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(128 * 4)
.order(ByteOrder.nativeOrder());
@ -185,20 +278,24 @@ public class TestLineOverlay extends RenderOverlay {
sbuf.put(indices);
sbuf.flip();
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferID);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, 18 * 2, sbuf, GLES20.GL_STATIC_DRAW);
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(box);
//sbuf.put(box);
sbuf.put(vertices);
sbuf.flip();
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBufferID);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, 56 * 2, sbuf, GLES20.GL_STATIC_DRAW);
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, 12, buf, GLES20.GL_STATIC_DRAW);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, flip.length, buf,
GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
mMapView.getMapViewPosition().getMapPosition(mMapPosition, null);
@ -213,12 +310,13 @@ public class TestLineOverlay extends RenderOverlay {
setMatrix(pos, mv);
Matrix.multiplyMM(mv, 0, proj, 0, mv, 0);
GLES20.glUseProgram(testProgram);
GLState.useProgram(testProgram);
GLES20.glDisable(GLES20.GL_CULL_FACE);
GLState.test(false, false);
GLState.enableVertexArrays(-1, -1);
GLES20.glEnableVertexAttribArray(htestVertexPosition0);
GLES20.glEnableVertexAttribArray(htestVertexPosition1);
GLES20.glEnableVertexAttribArray(htestVertexFlip);
GLES20.glUniformMatrix4fv(htestMatrix, 1, false, mv, 0);
@ -230,15 +328,23 @@ public class TestLineOverlay extends RenderOverlay {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBufferID);
GLES20.glVertexAttribPointer(htestVertexPosition0, 4, GLES20.GL_SHORT, false, 0, 8);
GLES20.glVertexAttribPointer(htestVertexPosition1, 4, GLES20.GL_SHORT, false, 0, 0);
GLES20.glUniform4f(htestColor, 0.5f, 0.5f, 1.0f, 1.0f);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 18, GLES20.GL_UNSIGNED_SHORT, 0);
GLES20.glVertexAttribPointer(htestVertexPosition0,
4, GLES20.GL_SHORT, false, 0, 8);
GLES20.glVertexAttribPointer(htestVertexPosition1,
4, GLES20.GL_SHORT, false, 0, 0);
GLES20.glUniform4f(htestColor, 0.5f, 0.5f, 1.0f, 1.0f);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0);
GLES20.glVertexAttribPointer(htestVertexPosition0,
4, GLES20.GL_SHORT, false, 0, 16);
GLES20.glVertexAttribPointer(htestVertexPosition1,
4, GLES20.GL_SHORT, false, 0, 8);
GLES20.glVertexAttribPointer(htestVertexPosition0, 4, GLES20.GL_SHORT, false, 0, 16);
GLES20.glVertexAttribPointer(htestVertexPosition1, 4, GLES20.GL_SHORT, false, 0, 8);
GLES20.glUniform4f(htestColor, 0.5f, 1.0f, 0.5f, 1.0f);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 18, GLES20.GL_UNSIGNED_SHORT, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);