PolygonLayer: separate drawing of stencil clip region

This commit is contained in:
Hannes Janetzek 2014-09-11 14:27:04 +02:00
parent 3cd2f9ea47
commit b69923e05d
3 changed files with 155 additions and 107 deletions

View File

@ -1,8 +1,10 @@
package org.oscim.layers.tile;
import static org.oscim.backend.GL20.GL_EQUAL;
import static org.oscim.layers.tile.MapTile.PROXY_GRAMPA;
import static org.oscim.layers.tile.MapTile.PROXY_PARENT;
import static org.oscim.layers.tile.MapTile.State.READY;
import static org.oscim.renderer.MapRenderer.COORD_SCALE;
import static org.oscim.renderer.elements.RenderElement.BITMAP;
import static org.oscim.renderer.elements.RenderElement.HAIRLINE;
import static org.oscim.renderer.elements.RenderElement.LINE;
@ -33,7 +35,8 @@ public class VectorTileRenderer extends TileRenderer {
protected int mClipMode;
protected GLMatrix mViewProj = new GLMatrix();
protected GLMatrix mClipProj = new GLMatrix();
protected GLMatrix mClipMVP = new GLMatrix();
/**
* Current number of frames drawn, used to not draw a
@ -47,10 +50,10 @@ public class VectorTileRenderer extends TileRenderer {
/* discard depth projection from tilt, depth buffer
* is used for clipping */
mViewProj.copy(v.proj);
mViewProj.setValue(10, 0);
mViewProj.setValue(14, 0);
mViewProj.multiplyRhs(v.view);
mClipProj.copy(v.proj);
mClipProj.setValue(10, 0);
mClipProj.setValue(14, 0);
mClipProj.multiplyRhs(v.view);
mClipMode = PolygonLayer.CLIP_STENCIL;
@ -64,11 +67,14 @@ public class VectorTileRenderer extends TileRenderer {
for (int i = 0; i < tileCnt; i++) {
MapTile t = tiles[i];
// TODO check if proxies are actually available
if (t.isVisible && t.state != READY) {
GL.glDepthMask(true);
GL.glClear(GL20.GL_DEPTH_BUFFER_BIT);
/* always write depth for non-proxy tiles */
/* always write depth for non-proxy tiles
* this is used in drawProxies pass to not
* draw where tiles were already drawn */
GL.glDepthFunc(GL20.GL_ALWAYS);
mClipMode = PolygonLayer.CLIP_DEPTH;
@ -152,6 +158,7 @@ public class VectorTileRenderer extends TileRenderer {
return;
layers.vbo.bind();
MapPosition pos = v.pos;
/* place tile relative to map position */
int z = tile.zoomLevel;
@ -163,25 +170,27 @@ public class VectorTileRenderer extends TileRenderer {
/* scale relative to zoom-level of this tile */
float scale = (float) (pos.scale / (1 << z));
v.mvp.setTransScale(x, y, scale / MapRenderer.COORD_SCALE);
v.mvp.multiplyLhs(mViewProj);
v.mvp.setTransScale(x, y, scale / COORD_SCALE);
v.mvp.multiplyLhs(v.viewproj);
boolean clipped = false;
int mode = mClipMode;
mClipMVP.setTransScale(x, y, scale / COORD_SCALE);
mClipMVP.multiplyLhs(mClipProj);
RenderElement l = layers.getBaseLayers();
PolygonLayer.Renderer.clip(mClipMVP, mClipMode);
boolean first = true;
while (l != null) {
if (l.type == POLYGON) {
l = PolygonLayer.Renderer.draw(l, v, div, !clipped, mode);
clipped = true;
l = PolygonLayer.Renderer.draw(l, v, div, first);
first = false;
/* set test for clip to tile region */
GL.glStencilFunc(GL_EQUAL, 0x80, 0x80);
// clipped = true;
continue;
}
if (!clipped) {
/* draw stencil buffer clip region */
PolygonLayer.Renderer.draw(null, v, div, true, mode);
clipped = true;
}
if (l.type == LINE) {
l = LineLayer.Renderer.draw(l, v, scale, layers);
continue;
@ -206,10 +215,6 @@ public class VectorTileRenderer extends TileRenderer {
l = layers.getTextureLayers();
while (l != null) {
if (!clipped) {
PolygonLayer.Renderer.draw(null, v, div, true, mode);
clipped = true;
}
if (l.type == BITMAP) {
l = BitmapLayer.Renderer.draw(l, v, 1, mLayerAlpha);
continue;
@ -219,12 +224,13 @@ public class VectorTileRenderer extends TileRenderer {
}
if (debugOverdraw) {
if (tile.zoomLevel > pos.zoomLevel)
PolygonLayer.Renderer.drawOver(v, Color.BLUE, 0.5f);
PolygonLayer.Renderer.drawOver(mClipMVP, Color.BLUE, 0.5f);
else if (tile.zoomLevel < pos.zoomLevel)
PolygonLayer.Renderer.drawOver(v, Color.RED, 0.5f);
PolygonLayer.Renderer.drawOver(mClipMVP, Color.RED, 0.5f);
else
PolygonLayer.Renderer.drawOver(v, Color.GREEN, 0.5f);
PolygonLayer.Renderer.drawOver(mClipMVP, Color.GREEN, 0.5f);
return;
}
@ -238,12 +244,12 @@ public class VectorTileRenderer extends TileRenderer {
long dTime = MapRenderer.frametime - tile.fadeTime;
if (mOverdrawColor == 0 || dTime > FADE_TIME) {
PolygonLayer.Renderer.drawOver(v, 0, 1);
PolygonLayer.Renderer.drawOver(mClipMVP, 0, 1);
return;
}
float fade = 1 - dTime / FADE_TIME;
PolygonLayer.Renderer.drawOver(v, mOverdrawColor, fade * fade);
PolygonLayer.Renderer.drawOver(mClipMVP, mOverdrawColor, fade * fade);
MapRenderer.animate();
}

View File

@ -100,7 +100,7 @@ public abstract class ElementRenderer extends LayerRenderer {
while (l != null) {
if (l.type == POLYGON) {
l = PolygonLayer.Renderer.draw(l, v, 1, true, 0);
l = PolygonLayer.Renderer.draw(l, v, 1, true);
continue;
}
if (l.type == LINE) {

View File

@ -16,16 +16,26 @@
*/
package org.oscim.renderer.elements;
import org.oscim.backend.GL20;
import static org.oscim.backend.GL20.GL_ALWAYS;
import static org.oscim.backend.GL20.GL_EQUAL;
import static org.oscim.backend.GL20.GL_INVERT;
import static org.oscim.backend.GL20.GL_KEEP;
import static org.oscim.backend.GL20.GL_REPLACE;
import static org.oscim.backend.GL20.GL_SHORT;
import static org.oscim.backend.GL20.GL_TRIANGLE_FAN;
import static org.oscim.backend.GL20.GL_TRIANGLE_STRIP;
import static org.oscim.backend.GL20.GL_ZERO;
import static org.oscim.utils.FastMath.clamp;
import org.oscim.core.GeometryBuffer;
import org.oscim.core.Tile;
import org.oscim.renderer.GLMatrix;
import org.oscim.renderer.GLShader;
import org.oscim.renderer.GLState;
import org.oscim.renderer.GLUtils;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.MapRenderer;
import org.oscim.theme.styles.AreaStyle;
import org.oscim.utils.FastMath;
import org.oscim.utils.math.Interpolation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -38,6 +48,7 @@ public final class PolygonLayer extends RenderElement {
public final static int CLIP_STENCIL = 1;
public final static int CLIP_DEPTH = 2;
public final static int CLIP_TEST_DEPTH = 3;
private static final float S = MapRenderer.COORD_SCALE;
@ -47,7 +58,6 @@ public final class PolygonLayer extends RenderElement {
PolygonLayer(int layer) {
super(RenderElement.POLYGON);
level = layer;
}
@ -105,8 +115,9 @@ public final class PolygonLayer extends RenderElement {
public static final class Renderer {
private static final int OPAQUE = 0xff000000;
private static final int STENCIL_BITS = 8;
private final static int CLIP_BIT = 0x80;
public final static int CLIP_BIT = 0x80;
private static final float FADE_START = 1.3f;
@ -134,15 +145,15 @@ public final class PolygonLayer extends RenderElement {
GL.glStencilMask(0x00);
//int shader = polyShader;
Shader s = setShader(polyShader, v, false);
Shader s = setShader(polyShader, v.mvp, false);
for (int c = start; c < end; c++) {
AreaStyle a = mAreaFills[c].current();
if (enableTexture && a.texture != null) {
s = setShader(texShader, v, false);
float num = FastMath.clamp((Tile.SIZE / a.texture.width) >> 1, 1, Tile.SIZE);
float transition = Interpolation.exp5.apply(FastMath.clamp(scale - 1, 0, 1));
s = setShader(texShader, v.mvp, false);
float num = clamp((Tile.SIZE / a.texture.width) >> 1, 1, Tile.SIZE);
float transition = Interpolation.exp5.apply(clamp(scale - 1, 0, 1));
GL.glUniform2f(s.uScale, transition, div / num);
//if (a.texture.alpha);
@ -184,29 +195,28 @@ public final class PolygonLayer extends RenderElement {
/* set stencil buffer mask used to draw this layer
* also check that clip bit is set to avoid overdraw
* of other tiles */
GL.glStencilFunc(GL20.GL_EQUAL, 0xff, CLIP_BIT | 1 << c);
GL.glStencilFunc(GL_EQUAL, 0xff, CLIP_BIT | 1 << c);
/* draw tile fill coordinates */
GL.glDrawArrays(GL20.GL_TRIANGLE_STRIP, 0, 4);
GL.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
/* disable texture shader */
if (s != polyShader)
s = setShader(polyShader, v, false);
s = setShader(polyShader, v.mvp, false);
}
}
// current layer to fill (0 - STENCIL_BITS-1)
/** current layer to fill (0 - STENCIL_BITS-1) */
private static int mCount;
/** must clear stencil for next draw */
private static boolean mClear;
private static Shader setShader(Shader shader, GLViewport v, boolean first) {
private static Shader setShader(Shader shader, GLMatrix mvp, boolean first) {
if (shader.useProgram() || first) {
GLState.enableVertexArrays(shader.aPos, -1);
GL.glVertexAttribPointer(shader.aPos, 2,
GL20.GL_SHORT, false, 0, 0);
v.mvp.setAsUniform(shader.uMVP);
GL_SHORT, false, 0, 0);
mvp.setAsUniform(shader.uMVP);
}
return shader;
}
@ -234,37 +244,40 @@ public final class PolygonLayer extends RenderElement {
* next layer
*/
public static RenderElement draw(RenderElement renderElement, GLViewport v,
float div, boolean first, int clipMode) {
float div, boolean first) {
GLState.test(false, true);
setShader(polyShader, v, first);
setShader(polyShader, v.mvp, first);
int zoom = v.pos.zoomLevel;
float scale = (float) v.pos.getZoomScale();
int cur = mCount;
int start = mCount;
/* reset start when only one layer left in stencil buffer */
if (first || cur > 5)
cur = 0;
/* draw to stencil buffer */
GL.glColorMask(false, false, false, false);
int start = cur;
/* op for stencil method polygon drawing */
GL.glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
boolean drawn = false;
RenderElement l = renderElement;
for (; l != null && l.type == RenderElement.POLYGON; l = l.next) {
for (; l != null && l.type == POLYGON; l = l.next) {
PolygonLayer pl = (PolygonLayer) l;
/* fade out polygon layers (set in RenderTheme) */
if (pl.area.fadeScale > 0 && pl.area.fadeScale > zoom)
continue;
if (cur == start) {
drawStencilRegion(first, clipMode);
first = false;
if (mClear) {
clearStencilRegion();
/* op for stencil method polygon drawing */
GL.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_INVERT);
GL.glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
start = cur = 0;
}
mAreaFills[cur] = pl.area.current();
@ -272,42 +285,48 @@ public final class PolygonLayer extends RenderElement {
/* set stencil mask to draw to */
GL.glStencilMask(1 << cur++);
GL.glDrawArrays(GL20.GL_TRIANGLE_FAN, l.offset, l.numVertices);
GL.glDrawArrays(GL_TRIANGLE_FAN, l.offset, l.numVertices);
/* draw up to 7 layers into stencil buffer */
if (cur == STENCIL_BITS - 1) {
//log.debug("fill1 {} {}", start, cur);
fillPolygons(v, start, cur, zoom, scale, div);
drawn = true;
mClear = true;
start = cur = 0;
if (l.next != null && l.next.type == POLYGON)
setShader(polyShader, v.mvp, false);
}
}
if (cur > 0)
if (cur > 0) {
//log.debug("fill2 {} {}", start, cur);
fillPolygons(v, start, cur, zoom, scale, div);
drawn = true;
}
if (clipMode > 0) {
if (first) {
drawStencilRegion(first, clipMode);
/* disable writes to stencil buffer */
GL.glStencilMask(0x00);
/* enable writes to color buffer */
GL.glColorMask(true, true, true, true);
} else {
/* set test for clip to tile region */
GL.glStencilFunc(GL20.GL_EQUAL, CLIP_BIT, CLIP_BIT);
}
if (!drawn) {
/* fillPolygons would re-enable color-mask
* but it's possible that all polygon layers
* were skipped */
GL.glColorMask(true, true, true, true);
GL.glStencilMask(0x00);
}
mCount = cur;
return l;
}
public static void clip(GLViewport v) {
setShader(polyShader, v, true);
public static void clip(GLMatrix mvp, int clipMode) {
setShader(polyShader, mvp, true);
drawStencilRegion(clipMode);
drawStencilRegion(true, 1);
/* disable writes to stencil buffer */
GL.glStencilMask(0x00);
/* enable writes to color buffer */
GL.glColorMask(true, true, true, true);
}
@ -319,7 +338,10 @@ public final class PolygonLayer extends RenderElement {
* @param first in the first run the clip region is set based on
* depth buffer and depth buffer is updated
*/
static void drawStencilRegion(boolean first, int clipMode) {
static void drawStencilRegion(int clipMode) {
//log.debug("draw stencil {}", clipMode);
mCount = 0;
mClear = false;
/* disable drawing to color buffer */
GL.glColorMask(false, false, false, false);
@ -327,41 +349,60 @@ public final class PolygonLayer extends RenderElement {
/* write to all stencil bits */
GL.glStencilMask(0xFF);
if (first) {
/* Draw clip-region into depth and stencil buffer.
* This is used for tile line and polygon layers.
*
* Together with depth test (GL_LESS) this ensures to
* only draw where no other tile has drawn yet. */
/* Draw clip-region into depth and stencil buffer.
* This is used for tile line and polygon layers.
*
* Together with depth test (GL_LESS) this ensures to
* only draw where no other tile has drawn yet. */
if (clipMode == CLIP_DEPTH) {
/* test GL_LESS/GL_ALWAYS to write to depth buffer */
GLState.test(true, true);
GL.glDepthMask(true);
}
if (clipMode == CLIP_DEPTH) {
/* tests GL_LESS/GL_ALWAYS */
GLState.test(true, true);
/* always pass stencil test and set clip bit */
GL.glStencilFunc(GL20.GL_ALWAYS, CLIP_BIT, 0x00);
/* write tile region to depth buffer */
GL.glDepthMask(true);
} else {
/* use clip bit from stencil buffer to clear stencil
* 'layer-bits' (0x7f) */
GL.glStencilFunc(GL20.GL_EQUAL, CLIP_BIT, CLIP_BIT);
GLState.test(false, true);
}
/* always pass stencil test and set clip bit */
GL.glStencilFunc(GL_ALWAYS, CLIP_BIT, 0x00);
/* set clip bit (0x80) for draw region */
GL.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_REPLACE);
GL.glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
/* draw a quad for the tile region */
GL.glDrawArrays(GL20.GL_TRIANGLE_STRIP, 0, 4);
GL.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
if (first) {
if (clipMode == CLIP_DEPTH) {
/* dont modify depth buffer */
GL.glDepthMask(false);
GLState.test(false, true);
}
GL.glStencilFunc(GL20.GL_EQUAL, CLIP_BIT, CLIP_BIT);
if (clipMode == CLIP_DEPTH) {
/* dont modify depth buffer */
GL.glDepthMask(false);
GLState.test(false, true);
}
GL.glStencilFunc(GL_EQUAL, CLIP_BIT, CLIP_BIT);
}
static void clearStencilRegion() {
//log.debug("clear stencil");
mCount = 0;
mClear = false;
/* disable drawing to color buffer */
GL.glColorMask(false, false, false, false);
/* write to all stencil bits */
GL.glStencilMask(0xFF);
/* use clip bit from stencil buffer to clear stencil
* 'layer-bits' (0x7f) */
GL.glStencilFunc(GL_EQUAL, CLIP_BIT, CLIP_BIT);
/* set clip bit (0x80) for draw region */
GL.glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
/* draw a quad for the tile region */
GL.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
/**
@ -369,12 +410,11 @@ public final class PolygonLayer extends RenderElement {
* a quad with func 'always' and op 'zero'. Using 'color'
* and 'alpha' to fake a fade effect.
*/
public static void drawOver(GLViewport v, int color, float alpha) {
public static void drawOver(GLMatrix mvp, int color, float alpha) {
// TODO true could be avoided when same shader and vbo
setShader(polyShader, v, true);
setShader(polyShader, mvp, true);
if (color == 0) {
/* disable drawing to framebuffer (will be re-enabled in fill) */
GL.glColorMask(false, false, false, false);
} else {
GLUtils.setColor(polyShader.uColor, color, alpha);
@ -384,17 +424,19 @@ public final class PolygonLayer extends RenderElement {
// TODO always pass stencil test: <-- only if not proxy?
//GL.glStencilFunc(GL_ALWAYS, 0x00, 0x00);
GL.glStencilFunc(GL20.GL_EQUAL, CLIP_BIT, CLIP_BIT);
GL.glStencilFunc(GL_EQUAL, CLIP_BIT, CLIP_BIT);
/* write to all bits */
GL.glStencilMask(0xFF);
// FIXME uneeded probably
GLState.test(false, true);
/* zero out area to draw to */
GL.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_ZERO);
GL.glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
GL.glDrawArrays(GL20.GL_TRIANGLE_STRIP, 0, 4);
GL.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// FIXME needed here?
if (color == 0)
GL.glColorMask(true, true, true, true);
}