From e32f45b585f55b3cb818db2a9a04894f844abd09 Mon Sep 17 00:00:00 2001
From: Hannes Janetzek <hannes.janetzek@gmail.com>
Date: Thu, 11 Sep 2014 13:29:41 +0200
Subject: [PATCH] S3DB/Buildings:

- allow multiple building extrusion buckets per tile
  (differently colored buildings depending on tags)
- fixed ExtrusionBucket index offsets
- rename more layer -> bucket
---
 vtm/src/org/oscim/layers/tile/TileLayer.java  |  10 ++
 .../layers/tile/buildings/BuildingLayer.java  |  74 +++++-----
 .../tile/buildings/BuildingRenderer.java      |  43 +++---
 .../layers/tile/buildings/S3DBLayer.java      |  46 ++----
 .../layers/tile/buildings/S3DBTileLoader.java |  23 ++-
 .../org/oscim/renderer/ExtrusionRenderer.java | 137 ++++++++----------
 .../org/oscim/renderer/OffscreenRenderer.java |   3 +-
 .../renderer/bucket/ExtrusionBucket.java      |  18 ++-
 .../renderer/bucket/ExtrusionBuckets.java     |  38 +++--
 9 files changed, 184 insertions(+), 208 deletions(-)

diff --git a/vtm/src/org/oscim/layers/tile/TileLayer.java b/vtm/src/org/oscim/layers/tile/TileLayer.java
index b3a37b0d..c5f95b2f 100644
--- a/vtm/src/org/oscim/layers/tile/TileLayer.java
+++ b/vtm/src/org/oscim/layers/tile/TileLayer.java
@@ -50,6 +50,16 @@ public abstract class TileLayer extends Layer implements UpdateListener {
 
 	}
 
+	public TileLayer(Map map, TileManager tileManager) {
+		super(map);
+		mTileManager = tileManager;
+	}
+
+	protected void setRenderer(TileRenderer renderer) {
+		renderer.setTileManager(mTileManager);
+		mRenderer = renderer;
+	}
+
 	abstract protected TileLoader createLoader();
 
 	public TileRenderer tileRenderer() {
diff --git a/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java b/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java
index bf447dc6..7414d677 100644
--- a/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java
+++ b/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java
@@ -24,7 +24,6 @@ import org.oscim.layers.tile.MapTile;
 import org.oscim.layers.tile.vector.VectorTileLayer;
 import org.oscim.layers.tile.vector.VectorTileLayer.TileLoaderThemeHook;
 import org.oscim.map.Map;
-import org.oscim.renderer.ExtrusionRenderer;
 import org.oscim.renderer.OffscreenRenderer;
 import org.oscim.renderer.OffscreenRenderer.Mode;
 import org.oscim.renderer.bucket.ExtrusionBucket;
@@ -32,6 +31,7 @@ import org.oscim.renderer.bucket.ExtrusionBuckets;
 import org.oscim.renderer.bucket.RenderBuckets;
 import org.oscim.theme.styles.ExtrusionStyle;
 import org.oscim.theme.styles.RenderStyle;
+import org.oscim.utils.pool.Inlist;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,39 +40,27 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
 
 	private final static int MIN_ZOOM = 17;
 	private final static int MAX_ZOOM = 17;
+
 	private final static boolean POST_AA = false;
+	private final static boolean TRANSLUCENT = true;
 
 	private static final Object BUILDING_DATA = BuildingLayer.class.getName();
 
-	private ExtrusionRenderer mExtRenderer;
-
 	public BuildingLayer(Map map, VectorTileLayer tileLayer) {
 		this(map, tileLayer, MIN_ZOOM, MAX_ZOOM);
-
-		//		super(map);
-		//		tileLayer.addHook(this);
-		//
-		//		mMinZoom = MIN_ZOOM;
-		//
-		//		OffscreenRenderer or = new OffscreenRenderer();
-		//		or.setRenderer(new ExtrusionRenderer(tileLayer.tileRenderer(), MIN_ZOOM));
-		//		mRenderer = or;
 	}
 
 	public BuildingLayer(Map map, VectorTileLayer tileLayer, int zoomMin, int zoomMax) {
+
 		super(map);
+
 		tileLayer.addHook(this);
 
-		mExtRenderer = new BuildingRenderer(tileLayer.tileRenderer(),
-		                                    zoomMin, zoomMax, false, true);
-
-		if (POST_AA) {
-			OffscreenRenderer or = new OffscreenRenderer(Mode.SSAO_FXAA);
-			or.setRenderer(mExtRenderer);
-			mRenderer = or;
-		} else {
-			mRenderer = mExtRenderer;
-		}
+		mRenderer = new BuildingRenderer(tileLayer.tileRenderer(),
+		                                 zoomMin, zoomMax,
+		                                 false, TRANSLUCENT);
+		if (POST_AA)
+			mRenderer = new OffscreenRenderer(Mode.SSAO_FXAA, mRenderer);
 	}
 
 	/** TileLoaderThemeHook */
@@ -91,36 +79,44 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
 		String v = element.tags.getValue(Tag.KEY_HEIGHT);
 		if (v != null)
 			height = Integer.parseInt(v);
+
 		v = element.tags.getValue(Tag.KEY_MIN_HEIGHT);
 		if (v != null)
 			minHeight = Integer.parseInt(v);
 
-		ExtrusionBuckets el = get(tile);
-
-		if (el.layers == null) {
-			double lat = MercatorProjection.toLatitude(tile.y);
-			float groundScale = (float) MercatorProjection
-			    .groundResolution(lat, 1 << tile.zoomLevel);
-
-			el.layers = new ExtrusionBucket(0, groundScale, extrusion.colors);
-		}
-
 		/* 12m default */
 		if (height == 0)
 			height = 12 * 100;
 
-		el.layers.add(element, height, minHeight);
+		ExtrusionBuckets ebs = get(tile);
+
+		for (ExtrusionBucket b = ebs.buckets; b != null; b = b.next()) {
+			if (b.colors == extrusion.colors) {
+				b.add(element, height, minHeight);
+				return true;
+			}
+		}
+
+		double lat = MercatorProjection.toLatitude(tile.y);
+		float groundScale = (float) MercatorProjection
+		    .groundResolution(lat, 1 << tile.zoomLevel);
+
+		ebs.buckets = Inlist.push(ebs.buckets,
+		                          new ExtrusionBucket(0, groundScale,
+		                                              extrusion.colors));
+
+		ebs.buckets.add(element, height, minHeight);
 
 		return true;
 	}
 
 	public static ExtrusionBuckets get(MapTile tile) {
-		ExtrusionBuckets el = (ExtrusionBuckets) tile.getData(BUILDING_DATA);
-		if (el == null) {
-			el = new ExtrusionBuckets(tile);
-			tile.addData(BUILDING_DATA, el);
+		ExtrusionBuckets eb = (ExtrusionBuckets) tile.getData(BUILDING_DATA);
+		if (eb == null) {
+			eb = new ExtrusionBuckets(tile);
+			tile.addData(BUILDING_DATA, eb);
 		}
-		return el;
+		return eb;
 	}
 
 	@Override
@@ -128,7 +124,7 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
 		if (success)
 			get(tile).prepare();
 		else
-			get(tile).setLayers(null);
+			get(tile).setBuckets(null);
 	}
 
 	//	private int multi;
diff --git a/vtm/src/org/oscim/layers/tile/buildings/BuildingRenderer.java b/vtm/src/org/oscim/layers/tile/buildings/BuildingRenderer.java
index 4104eefa..745f167a 100644
--- a/vtm/src/org/oscim/layers/tile/buildings/BuildingRenderer.java
+++ b/vtm/src/org/oscim/layers/tile/buildings/BuildingRenderer.java
@@ -101,8 +101,9 @@ public class BuildingRenderer extends ExtrusionRenderer {
 		TileDistanceSort.sort(tiles, 0, mTileSet.cnt);
 
 		/* keep a list of tiles available for rendering */
-		if (mExtrusionLayerSet == null || mExtrusionLayerSet.length < mTileSet.cnt * 4)
-			mExtrusionLayerSet = new ExtrusionBuckets[mTileSet.cnt * 4];
+		int maxTiles = mTileSet.cnt * 4;
+		if (mExtrusionBucketSet.length < maxTiles)
+			mExtrusionBucketSet = new ExtrusionBuckets[maxTiles];
 
 		/* compile one tile max per frame */
 		boolean compiled = false;
@@ -114,14 +115,14 @@ public class BuildingRenderer extends ExtrusionRenderer {
 			/* TODO - if tile is not available try parent or children */
 
 			for (int i = 0; i < mTileSet.cnt; i++) {
-				ExtrusionBuckets els = getLayer(tiles[i]);
-				if (els == null)
+				ExtrusionBuckets ebs = getBuckets(tiles[i]);
+				if (ebs == null)
 					continue;
 
-				if (els.compiled)
-					mExtrusionLayerSet[activeTiles++] = els;
-				else if (!compiled && els.compileLayers()) {
-					mExtrusionLayerSet[activeTiles++] = els;
+				if (ebs.compiled)
+					mExtrusionBucketSet[activeTiles++] = ebs;
+				else if (!compiled && ebs.compile()) {
+					mExtrusionBucketSet[activeTiles++] = ebs;
 					compiled = true;
 				}
 			}
@@ -137,15 +138,15 @@ public class BuildingRenderer extends ExtrusionRenderer {
 				//		if (c == t)
 				//			continue O;
 
-				ExtrusionBuckets els = getLayer(t);
-				if (els == null)
+				ExtrusionBuckets ebs = getBuckets(t);
+				if (ebs == null)
 					continue;
 
-				if (els.compiled)
-					mExtrusionLayerSet[activeTiles++] = els;
+				if (ebs.compiled)
+					mExtrusionBucketSet[activeTiles++] = ebs;
 
-				else if (!compiled && els.compileLayers()) {
-					mExtrusionLayerSet[activeTiles++] = els;
+				else if (!compiled && ebs.compile()) {
+					mExtrusionBucketSet[activeTiles++] = ebs;
 					compiled = true;
 				}
 			}
@@ -158,12 +159,12 @@ public class BuildingRenderer extends ExtrusionRenderer {
 						continue;
 
 					MapTile c = t.node.child(j);
-					ExtrusionBuckets el = getLayer(c);
+					ExtrusionBuckets eb = getBuckets(c);
 
-					if (el == null || !el.compiled)
+					if (eb == null || !eb.compiled)
 						continue;
 
-					mExtrusionLayerSet[activeTiles++] = el;
+					mExtrusionBucketSet[activeTiles++] = eb;
 				}
 			}
 		}
@@ -172,7 +173,7 @@ public class BuildingRenderer extends ExtrusionRenderer {
 		if (compiled)
 			MapRenderer.animate();
 
-		mExtrusionLayerCnt = activeTiles;
+		mBucketsCnt = activeTiles;
 
 		//log.debug("active tiles: {}", mExtrusionLayerCnt);
 
@@ -192,9 +193,9 @@ public class BuildingRenderer extends ExtrusionRenderer {
 		mTileRenderer.releaseTiles(mTileSet);
 	}
 
-	private static ExtrusionBuckets getLayer(MapTile t) {
-		RenderBuckets layers = t.getBuckets();
-		if (layers != null && !t.state(READY | NEW_DATA))
+	private static ExtrusionBuckets getBuckets(MapTile t) {
+		RenderBuckets buckets = t.getBuckets();
+		if (buckets != null && !t.state(READY | NEW_DATA))
 			return null;
 
 		return BuildingLayer.get(t);
diff --git a/vtm/src/org/oscim/layers/tile/buildings/S3DBLayer.java b/vtm/src/org/oscim/layers/tile/buildings/S3DBLayer.java
index 38a5aeb8..b306e7cc 100644
--- a/vtm/src/org/oscim/layers/tile/buildings/S3DBLayer.java
+++ b/vtm/src/org/oscim/layers/tile/buildings/S3DBLayer.java
@@ -6,6 +6,7 @@ import org.oscim.layers.tile.TileManager;
 import org.oscim.layers.tile.TileRenderer;
 import org.oscim.map.Map;
 import org.oscim.renderer.GLViewport;
+import org.oscim.renderer.LayerRenderer;
 import org.oscim.renderer.OffscreenRenderer;
 import org.oscim.renderer.OffscreenRenderer.Mode;
 import org.oscim.tiling.TileSource;
@@ -28,9 +29,8 @@ public class S3DBLayer extends TileLayer {
 	private final TileSource mTileSource;
 
 	public S3DBLayer(Map map, TileSource tileSource) {
-		super(map,
-		      new TileManager(map, SRC_ZOOM, SRC_ZOOM, MAX_CACHE),
-		      new S3DBRenderer());
+		super(map, new TileManager(map, SRC_ZOOM, SRC_ZOOM, MAX_CACHE));
+		setRenderer(new S3DBRenderer());
 
 		mTileSource = tileSource;
 		initLoader(2);
@@ -42,48 +42,30 @@ public class S3DBLayer extends TileLayer {
 	}
 
 	public static class S3DBRenderer extends TileRenderer {
-		BuildingRenderer mExtRenderer;
-		OffscreenRenderer or;
+		LayerRenderer mRenderer;
 
 		public S3DBRenderer() {
-			mExtRenderer = new BuildingRenderer(this, SRC_ZOOM, SRC_ZOOM, true, false);
+			mRenderer = new BuildingRenderer(this, SRC_ZOOM, SRC_ZOOM, true, false);
 
-			if (POST_FXAA) {
-				//or = new OffscreenRenderer(Mode.FXAA);
-				or = new OffscreenRenderer(Mode.SSAO_FXAA);
-				or.setRenderer(mExtRenderer);
-			}
+			if (POST_FXAA)
+				mRenderer = new OffscreenRenderer(Mode.SSAO_FXAA, mRenderer);
 		}
 
 		@Override
-		protected synchronized void update(GLViewport v) {
+		public synchronized void update(GLViewport v) {
 			super.update(v);
-			if (POST_FXAA) {
-				or.update(v);
-				setReady(or.isReady());
-			} else {
-				mExtRenderer.update(v);
-				setReady(mExtRenderer.isReady());
-			}
+			mRenderer.update(v);
+			setReady(mRenderer.isReady());
 		}
 
 		@Override
-		protected synchronized void render(GLViewport v) {
-			if (POST_FXAA) {
-				or.render(v);
-			} else {
-				mExtRenderer.render(v);
-			}
+		public synchronized void render(GLViewport v) {
+			mRenderer.render(v);
 		}
 
 		@Override
-		protected boolean setup() {
-			if (POST_FXAA) {
-				or.setup();
-			} else {
-				mExtRenderer.setup();
-			}
-
+		public boolean setup() {
+			mRenderer.setup();
 			return super.setup();
 		}
 	}
diff --git a/vtm/src/org/oscim/layers/tile/buildings/S3DBTileLoader.java b/vtm/src/org/oscim/layers/tile/buildings/S3DBTileLoader.java
index 1dabccb6..8296ee5b 100644
--- a/vtm/src/org/oscim/layers/tile/buildings/S3DBTileLoader.java
+++ b/vtm/src/org/oscim/layers/tile/buildings/S3DBTileLoader.java
@@ -12,7 +12,6 @@ import org.oscim.layers.tile.MapTile;
 import org.oscim.layers.tile.TileLoader;
 import org.oscim.layers.tile.TileManager;
 import org.oscim.renderer.bucket.ExtrusionBucket;
-import org.oscim.renderer.bucket.ExtrusionBuckets;
 import org.oscim.tiling.ITileDataSource;
 import org.oscim.tiling.TileSource;
 import org.slf4j.Logger;
@@ -24,7 +23,7 @@ class S3DBTileLoader extends TileLoader {
 	/** current TileDataSource used by this MapTileLoader */
 	private final ITileDataSource mTileDataSource;
 
-	private ExtrusionBucket mLayers;
+	private ExtrusionBucket mParts;
 	private ExtrusionBucket mRoofs;
 
 	private float mGroundScale;
@@ -77,13 +76,11 @@ class S3DBTileLoader extends TileLoader {
 
 		mRoofs = new ExtrusionBucket(0, mGroundScale, Color.get(247, 249, 250));
 
-		mLayers = new ExtrusionBucket(0, mGroundScale, Color.get(255, 254, 252));
+		mParts = new ExtrusionBucket(0, mGroundScale, Color.get(255, 254, 252));
 		//mRoofs = new ExtrusionLayer(0, mGroundScale, Color.get(207, 209, 210));
-		mRoofs.next = mLayers;
+		mRoofs.next = mParts;
 
-		ExtrusionBuckets layers = BuildingLayer.get(tile);
-
-		layers.setLayers(mRoofs);
+		BuildingLayer.get(tile).setBuckets(mRoofs);
 
 		process(mTilePlane);
 	}
@@ -101,7 +98,7 @@ class S3DBTileLoader extends TileLoader {
 			return;
 		}
 
-		if (mLayers == null)
+		if (mParts == null)
 			initTile(mTile);
 
 		boolean isRoof = element.tags.containsKey(ROOF_KEY);
@@ -123,11 +120,11 @@ class S3DBTileLoader extends TileLoader {
 			if (isRoof && (roofShape == null || "flat".equals(roofShape)))
 				mRoofs.add(element);
 			else
-				mLayers.add(element);
+				mParts.add(element);
 			return;
 		}
 
-		for (ExtrusionBucket l = mLayers; l != null; l = l.next()) {
+		for (ExtrusionBucket l = mParts; l != null; l = l.next()) {
 			if (l.color == c) {
 				l.add(element);
 				return;
@@ -135,8 +132,8 @@ class S3DBTileLoader extends TileLoader {
 		}
 		ExtrusionBucket l = new ExtrusionBucket(0, mGroundScale, c);
 
-		l.next = mLayers.next;
-		mLayers.next = l;
+		l.next = mParts.next;
+		mParts.next = l;
 
 		l.add(element);
 	}
@@ -144,7 +141,7 @@ class S3DBTileLoader extends TileLoader {
 	@Override
 	public void completed(QueryResult result) {
 
-		mLayers = null;
+		mParts = null;
 		mRoofs = null;
 
 		super.completed(result);
diff --git a/vtm/src/org/oscim/renderer/ExtrusionRenderer.java b/vtm/src/org/oscim/renderer/ExtrusionRenderer.java
index e66258a0..6f36f0da 100644
--- a/vtm/src/org/oscim/renderer/ExtrusionRenderer.java
+++ b/vtm/src/org/oscim/renderer/ExtrusionRenderer.java
@@ -16,6 +16,17 @@
  */
 package org.oscim.renderer;
 
+import static org.oscim.backend.GL20.GL_CULL_FACE;
+import static org.oscim.backend.GL20.GL_DEPTH_BUFFER_BIT;
+import static org.oscim.backend.GL20.GL_EQUAL;
+import static org.oscim.backend.GL20.GL_LEQUAL;
+import static org.oscim.backend.GL20.GL_LESS;
+import static org.oscim.backend.GL20.GL_LINES;
+import static org.oscim.backend.GL20.GL_SHORT;
+import static org.oscim.backend.GL20.GL_TRIANGLES;
+import static org.oscim.backend.GL20.GL_UNSIGNED_BYTE;
+import static org.oscim.backend.GL20.GL_UNSIGNED_SHORT;
+
 import org.oscim.backend.GL20;
 import org.oscim.core.Tile;
 import org.oscim.renderer.bucket.ExtrusionBucket;
@@ -30,8 +41,8 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
 	private final int mMode;
 	private Shader mShader;
 
-	protected ExtrusionBuckets[] mExtrusionLayerSet;
-	protected int mExtrusionLayerCnt;
+	protected ExtrusionBuckets[] mExtrusionBucketSet = {};
+	protected int mBucketsCnt;
 	protected float mAlpha = 1;
 
 	public ExtrusionRenderer(boolean mesh, boolean alpha) {
@@ -65,22 +76,25 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
 		return true;
 	}
 
-	private void renderCombined(int vertexPointer, ExtrusionBuckets els) {
+	private void renderCombined(int vertexPointer, ExtrusionBuckets ebs) {
 
-		for (ExtrusionBucket el = els.layers; el != null; el = el.next()) {
+		for (ExtrusionBucket eb = ebs.buckets(); eb != null; eb = eb.next()) {
 
 			GL.glVertexAttribPointer(vertexPointer, 3,
-			                         GL20.GL_SHORT, false, 8, 0);
+			                         GL_SHORT, false, 8,
+			                         eb.getVertexOffset());
 
-			int sumIndices = el.idx[0] + el.idx[1] + el.idx[2];
+			int sumIndices = eb.idx[0] + eb.idx[1] + eb.idx[2];
+
+			/* extrusion */
 			if (sumIndices > 0)
-				GL.glDrawElements(GL20.GL_TRIANGLES, sumIndices,
-				                  GL20.GL_UNSIGNED_SHORT, 0);
+				GL.glDrawElements(GL_TRIANGLES, sumIndices,
+				                  GL_UNSIGNED_SHORT, eb.off[0]);
 
-			if (el.idx[2] > 0) {
-				int offset = sumIndices * 2;
-				GL.glDrawElements(GL20.GL_TRIANGLES, el.idx[4],
-				                  GL20.GL_UNSIGNED_SHORT, offset);
+			/* mesh */
+			if (eb.idx[4] > 0) {
+				GL.glDrawElements(GL_TRIANGLES, eb.idx[4],
+				                  GL_UNSIGNED_SHORT, eb.off[4]);
 			}
 		}
 	}
@@ -89,7 +103,7 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
 	public void render(GLViewport v) {
 
 		GL.glDepthMask(true);
-		GL.glClear(GL20.GL_DEPTH_BUFFER_BIT);
+		GL.glClear(GL_DEPTH_BUFFER_BIT);
 
 		GLState.test(true, false);
 
@@ -100,12 +114,12 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
 		/* only use face-culling when it's unlikely
 		 * that one'moves through the building' */
 		if (v.pos.zoomLevel < 18)
-			GL.glEnable(GL20.GL_CULL_FACE);
+			GL.glEnable(GL_CULL_FACE);
 
-		GL.glDepthFunc(GL20.GL_LESS);
+		GL.glDepthFunc(GL_LESS);
 		GL.glUniform1f(s.uAlpha, mAlpha);
 
-		ExtrusionBuckets[] els = mExtrusionLayerSet;
+		ExtrusionBuckets[] ebs = mExtrusionBucketSet;
 
 		if (mTranslucent) {
 			/* only draw to depth buffer */
@@ -113,24 +127,24 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
 			GL.glColorMask(false, false, false, false);
 			GL.glUniform1i(s.uMode, -1);
 
-			for (int i = 0; i < mExtrusionLayerCnt; i++) {
-				if (els[i].ibo == null)
+			for (int i = 0; i < mBucketsCnt; i++) {
+				if (ebs[i].ibo == null)
 					return;
 
-				els[i].ibo.bind();
-				els[i].vbo.bind();
+				ebs[i].ibo.bind();
+				ebs[i].vbo.bind();
 
-				setMatrix(v, els[i], true);
+				setMatrix(v, ebs[i], true);
 				v.mvp.setAsUniform(s.uMVP);
 
-				renderCombined(s.aPos, els[i]);
+				renderCombined(s.aPos, ebs[i]);
 			}
 
 			/* only draw to color buffer */
 			GL.glColorMask(true, true, true, true);
 			GL.glDepthMask(false);
 
-			GL.glDepthFunc(GL20.GL_EQUAL);
+			GL.glDepthFunc(GL_EQUAL);
 		}
 
 		GLState.blend(true);
@@ -138,108 +152,83 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
 		GLState.enableVertexArrays(s.aPos, s.aLight);
 		float[] currentColor = null;
 
-		for (int i = 0; i < mExtrusionLayerCnt; i++) {
-			if (els[i].ibo == null)
+		for (int i = 0; i < mBucketsCnt; i++) {
+			if (ebs[i].ibo == null)
 				continue;
 
-			els[i].ibo.bind();
-			els[i].vbo.bind();
+			ebs[i].ibo.bind();
+			ebs[i].vbo.bind();
 
 			if (!mTranslucent) {
-				setMatrix(v, els[i], false);
+				setMatrix(v, ebs[i], false);
 				v.mvp.setAsUniform(s.uMVP);
 			}
 
-			ExtrusionBucket el = els[i].getLayers();
-			for (; el != null; el = el.next()) {
+			ExtrusionBucket eb = ebs[i].buckets();
 
-				if (el.colors != currentColor) {
-					currentColor = el.colors;
+			for (; eb != null; eb = eb.next()) {
+
+				if (eb.colors != currentColor) {
+					currentColor = eb.colors;
 					GLUtils.glUniform4fv(s.uColor,
 					                     mMode == 0 ? 4 : 1,
-					                     el.colors);
+					                     eb.colors);
 				}
+				GL.glVertexAttribPointer(s.aPos, 3, GL_SHORT,
+				                         false, 8, eb.getVertexOffset());
 
-				GL.glVertexAttribPointer(s.aPos, 3, GL20.GL_SHORT,
-				                         false, 8, el.getVertexOffset());
-
-				GL.glVertexAttribPointer(s.aLight, 2,
-				                         GL20.GL_UNSIGNED_BYTE,
-				                         false, 8, el.getVertexOffset() + 6);
+				GL.glVertexAttribPointer(s.aLight, 2, GL_UNSIGNED_BYTE,
+				                         false, 8, eb.getVertexOffset() + 6);
 
 				/* draw extruded outlines */
-				if (el.idx[0] > 0) {
+				if (eb.idx[0] > 0) {
 					if (mTranslucent) {
 						GL.glDepthFunc(GL20.GL_EQUAL);
-						setMatrix(v, els[i], true);
+						setMatrix(v, ebs[i], true);
 						v.mvp.setAsUniform(s.uMVP);
 					}
 
 					/* draw roof */
 					GL.glUniform1i(s.uMode, 0);
-					GL.glDrawElements(GL20.GL_TRIANGLES,
-					                  el.idx[2],
-					                  GL20.GL_UNSIGNED_SHORT,
-					                  (el.idx[0]
-					                  + el.idx[1]) * 2);
+					GL.glDrawElements(GL_TRIANGLES, eb.idx[2], GL_UNSIGNED_SHORT, eb.off[2]);
 
 					/* draw sides 1 */
 					GL.glUniform1i(s.uMode, 1);
-					GL.glDrawElements(GL20.GL_TRIANGLES,
-					                  el.idx[0],
-					                  GL20.GL_UNSIGNED_SHORT, 0);
+					GL.glDrawElements(GL_TRIANGLES, eb.idx[0], GL_UNSIGNED_SHORT, eb.off[0]);
 
 					/* draw sides 2 */
 					GL.glUniform1i(s.uMode, 2);
-					GL.glDrawElements(GL20.GL_TRIANGLES,
-					                  el.idx[1],
-					                  GL20.GL_UNSIGNED_SHORT,
-					                  el.idx[0] * 2);
+					GL.glDrawElements(GL_TRIANGLES, eb.idx[1], GL_UNSIGNED_SHORT, eb.off[1]);
 
 					if (mTranslucent) {
 						/* drawing gl_lines with the same coordinates does not
 						 * result in same depth values as polygons, so add
 						 * offset and draw gl_lequal: */
-						GL.glDepthFunc(GL20.GL_LEQUAL);
+						GL.glDepthFunc(GL_LEQUAL);
 						v.mvp.addDepthOffset(100);
 						v.mvp.setAsUniform(s.uMVP);
 					}
 
 					GL.glUniform1i(s.uMode, 3);
-					int offset = 2 * (el.indexOffset
-					        + el.idx[0] + el.idx[1]
-					        + el.idx[2]);
-
-					GL.glDrawElements(GL20.GL_LINES,
-					                  el.idx[3],
-					                  GL20.GL_UNSIGNED_SHORT,
-					                  offset);
 
+					GL.glDrawElements(GL_LINES, eb.idx[3], GL_UNSIGNED_SHORT, eb.off[3]);
 				}
 
 				/* draw triangle meshes */
-				if (el.idx[4] > 0) {
-					int offset = 2 * (el.indexOffset
-					        + el.idx[0] + el.idx[1]
-					        + el.idx[2] + el.idx[3]);
-
-					GL.glDrawElements(GL20.GL_TRIANGLES,
-					                  el.idx[4],
-					                  GL20.GL_UNSIGNED_SHORT,
-					                  offset);
+				if (eb.idx[4] > 0) {
+					GL.glDrawElements(GL_TRIANGLES, eb.idx[4], GL_UNSIGNED_SHORT, eb.off[4]);
 				}
 			}
 
 			/* just a temporary reference! */
-			els[i] = null;
+			ebs[i] = null;
 		}
 
 		if (!mTranslucent)
 			GL.glDepthMask(false);
 
 		if (v.pos.zoomLevel < 18)
-			GL.glDisable(GL20.GL_CULL_FACE);
-
+			GL.glDisable(GL_CULL_FACE);
 	}
 
 	private static void setMatrix(GLViewport v, ExtrusionBuckets l, boolean offset) {
diff --git a/vtm/src/org/oscim/renderer/OffscreenRenderer.java b/vtm/src/org/oscim/renderer/OffscreenRenderer.java
index f4a02636..b873e92a 100644
--- a/vtm/src/org/oscim/renderer/OffscreenRenderer.java
+++ b/vtm/src/org/oscim/renderer/OffscreenRenderer.java
@@ -44,10 +44,11 @@ public class OffscreenRenderer extends LayerRenderer {
 
 	public final Mode mode;
 
-	public OffscreenRenderer(Mode mode) {
+	public OffscreenRenderer(Mode mode, LayerRenderer renderer) {
 		this.mode = mode;
 		if (mode == Mode.SSAO || mode == Mode.SSAO_FXAA)
 			useDepthTexture = true;
+		setRenderer(renderer);
 	}
 
 	protected boolean setupFBO(GLViewport viewport) {
diff --git a/vtm/src/org/oscim/renderer/bucket/ExtrusionBucket.java b/vtm/src/org/oscim/renderer/bucket/ExtrusionBucket.java
index 760d9955..671d511f 100644
--- a/vtm/src/org/oscim/renderer/bucket/ExtrusionBucket.java
+++ b/vtm/src/org/oscim/renderer/bucket/ExtrusionBucket.java
@@ -47,6 +47,8 @@ public class ExtrusionBucket extends RenderBucket {
 
 	/** indices for: 0. even sides, 1. odd sides, 2. roof, 3. roof outline */
 	public int idx[] = { 0, 0, 0, 0, 0 };
+	/** indices offsets in bytes */
+	public int off[] = { 0, 0, 0, 0, 0 };
 
 	//private final static int IND_EVEN_SIDE = 0;
 	//private final static int IND_ODD_SIDE = 1;
@@ -60,8 +62,6 @@ public class ExtrusionBucket extends RenderBucket {
 
 	private KeyMap<Vertex> mVertexMap;
 
-	public int indexOffset;
-
 	//private static final int NORMAL_DIR_MASK = 0xFFFFFFFE;
 	//private int numIndexHits = 0;
 
@@ -573,22 +573,24 @@ public class ExtrusionBucket extends RenderBucket {
 		if (numVertices == 0)
 			return;
 
-		indexOffset = iboData.position();
+		indiceOffset = iboData.position();
 
+		int iOffset = indiceOffset;
 		for (int i = 0; i <= IND_MESH; i++) {
-			if (mIndices[i] == null)
-				continue;
-			idx[i] += mIndices[i].compile(iboData);
+			if (mIndices[i] != null) {
+				idx[i] = mIndices[i].compile(iboData);
+				off[i] = iOffset * 2;
+				iOffset += idx[i];
+			}
 		}
 		vertexOffset = vboData.position() * 2;
-
 		vertexItems.compile(vboData);
 
 		clear();
 	}
 
 	@Override
-	protected void clear() {
+	public void clear() {
 		mClipper = null;
 		releaseVertexPool();
 
diff --git a/vtm/src/org/oscim/renderer/bucket/ExtrusionBuckets.java b/vtm/src/org/oscim/renderer/bucket/ExtrusionBuckets.java
index cbf35981..b3974774 100644
--- a/vtm/src/org/oscim/renderer/bucket/ExtrusionBuckets.java
+++ b/vtm/src/org/oscim/renderer/bucket/ExtrusionBuckets.java
@@ -13,7 +13,7 @@ import org.slf4j.LoggerFactory;
 public class ExtrusionBuckets extends TileData {
 	static final Logger log = LoggerFactory.getLogger(ExtrusionBuckets.class);
 
-	public ExtrusionBucket layers;
+	public ExtrusionBucket buckets;
 
 	public boolean compiled;
 
@@ -34,20 +34,20 @@ public class ExtrusionBuckets extends TileData {
 	/**
 	 * Set new ExtrusionLayers and clear previous.
 	 */
-	public void setLayers(ExtrusionBucket el) {
-		for (RenderBucket l = layers; l != null; l = l.next)
-			l.clear();
+	public void setBuckets(ExtrusionBucket el) {
+		for (RenderBucket b = buckets; b != null; b = b.next)
+			b.clear();
 
-		layers = el;
+		buckets = el;
 	}
 
-	public ExtrusionBucket getLayers() {
-		return layers;
+	public ExtrusionBucket buckets() {
+		return buckets;
 	}
 
 	@Override
 	protected void dispose() {
-		setLayers(null);
+		setBuckets(null);
 
 		if (compiled) {
 			ibo = BufferObject.release(ibo);
@@ -57,21 +57,21 @@ public class ExtrusionBuckets extends TileData {
 	}
 
 	public void prepare() {
-		for (RenderBucket l = layers; l != null; l = l.next)
-			l.prepare();
+		for (RenderBucket b = buckets; b != null; b = b.next)
+			b.prepare();
 	}
 
-	public boolean compileLayers() {
+	public boolean compile() {
 
-		if (layers == null)
+		if (buckets == null)
 			return false;
 
 		int sumIndices = 0;
 		int sumVertices = 0;
 
-		for (ExtrusionBucket l = layers; l != null; l = l.next()) {
-			sumIndices += l.numIndices;
-			sumVertices += l.numVertices;
+		for (ExtrusionBucket b = buckets; b != null; b = b.next()) {
+			sumIndices += b.numIndices;
+			sumVertices += b.numVertices;
 		}
 		if (sumIndices == 0)
 			return false;
@@ -79,9 +79,9 @@ public class ExtrusionBuckets extends TileData {
 		ShortBuffer vboData = MapRenderer.getShortBuffer(sumVertices * 4);
 		ShortBuffer iboData = MapRenderer.getShortBuffer(sumIndices);
 
-		for (ExtrusionBucket l = layers; l != null; l = l.next()) {
-			l.compile(vboData, iboData);
-		}
+		for (ExtrusionBucket b = buckets; b != null; b = b.next())
+			b.compile(vboData, iboData);
+
 		int size = sumIndices * 2;
 		if (iboData.position() != sumIndices) {
 			int pos = iboData.position();
@@ -90,7 +90,6 @@ public class ExtrusionBuckets extends TileData {
 		}
 		ibo = BufferObject.get(GL20.GL_ELEMENT_ARRAY_BUFFER, size);
 		ibo.loadBufferData(iboData.flip(), size);
-		ibo.unbind();
 
 		size = sumVertices * 4 * 2;
 		if (vboData.position() != sumVertices * 4) {
@@ -101,7 +100,6 @@ public class ExtrusionBuckets extends TileData {
 
 		vbo = BufferObject.get(GL20.GL_ARRAY_BUFFER, size);
 		vbo.loadBufferData(vboData.flip(), size);
-		vbo.unbind();
 
 		compiled = true;