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