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

View File

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

View File

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