-rename BuildingOverlay -> ExtrusionOverlay,

even if there is probably nothing else to extrude
- started to draw extrusion layer for proxy tiles
- fixed depth offsetting..
This commit is contained in:
Hannes Janetzek 2013-01-05 02:46:27 +01:00
parent 0c023f9989
commit 99ce02a9bd
6 changed files with 400 additions and 386 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012 Hannes Janetzek * Copyright 2012, 2013 OpenScienceMap
* *
* This program is free software: you can redistribute it and/or modify it under the * This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General License as published by the Free Software * terms of the GNU Lesser General License as published by the Free Software
@ -39,6 +39,7 @@ import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.Layers; import org.oscim.renderer.layer.Layers;
import org.oscim.renderer.overlays.RenderOverlay; import org.oscim.renderer.overlays.RenderOverlay;
import org.oscim.theme.RenderTheme; import org.oscim.theme.RenderTheme;
import org.oscim.utils.FastMath;
import org.oscim.utils.GlUtils; import org.oscim.utils.GlUtils;
import org.oscim.view.MapView; import org.oscim.view.MapView;
import org.oscim.view.MapViewPosition; import org.oscim.view.MapViewPosition;
@ -49,9 +50,12 @@ import android.opengl.Matrix;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log; import android.util.Log;
/**
* @author Hannes Janetzek
*/
public class GLRenderer implements GLSurfaceView.Renderer { public class GLRenderer implements GLSurfaceView.Renderer {
private static final String TAG = "SurfaceRenderer"; private static final String TAG = GLRenderer.class.getName();
private static final int MB = 1024 * 1024; private static final int MB = 1024 * 1024;
private static final int SHORT_BYTES = 2; private static final int SHORT_BYTES = 2;
@ -86,7 +90,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
private static float[] mClearColor = null; private static float[] mClearColor = null;
// number of tiles drawn in one frame // number of tiles drawn in one frame
private static short mDrawCount = 0; //private static short mDrawCount = 0;
private static boolean mUpdateColor = false; private static boolean mUpdateColor = false;
@ -356,16 +360,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
Matrix.multiplyMM(matrix, 0, mfProjMatrix, 0, matrix, 0); Matrix.multiplyMM(matrix, 0, mfProjMatrix, 0, matrix, 0);
} }
private static float scaleDiv(MapTile t) {
float div = 1;
int diff = mMapPosition.zoomLevel - t.zoomLevel;
if (diff < 0)
div = (1 << -diff);
else if (diff > 0)
div = (1.0f / (1 << diff));
return div;
}
@Override @Override
public void onDrawFrame(GL10 glUnused) { public void onDrawFrame(GL10 glUnused) {
@ -435,7 +429,8 @@ public class GLRenderer implements GLSurfaceView.Renderer {
// relative zoom-level, 'tiles' could not have been updated after // relative zoom-level, 'tiles' could not have been updated after
// zoom-level changed. // zoom-level changed.
float div = scaleDiv(tiles[0]); byte z = tiles[0].zoomLevel;
float div = FastMath.pow(z - mapPosition.zoomLevel);
// transform screen coordinates to tile coordinates // transform screen coordinates to tile coordinates
float scale = mapPosition.scale / div; float scale = mapPosition.scale / div;
@ -448,7 +443,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
} }
mHolderCount = 0; mHolderCount = 0;
mScanBox.scan(coords, tiles[0].zoomLevel); mScanBox.scan(coords, z);
} }
tileCnt += mHolderCount; tileCnt += mHolderCount;
@ -507,7 +502,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
/* draw base layer */ /* draw base layer */
GLES20.glEnable(GL_DEPTH_TEST); GLES20.glEnable(GL_DEPTH_TEST);
GLES20.glEnable(GL_POLYGON_OFFSET_FILL); GLES20.glEnable(GL_POLYGON_OFFSET_FILL);
mDrawCount = 0; // mDrawCount = 0;
for (int i = 0; i < tileCnt; i++) { for (int i = 0; i < tileCnt; i++) {
MapTile t = tiles[i]; MapTile t = tiles[i];
@ -589,6 +584,10 @@ public class GLRenderer implements GLSurfaceView.Renderer {
} }
} }
public static int depthOffset(MapTile t) {
return ((t.tileX % 4) + (t.tileY % 4 * 4) * 2) * 20;
}
// used to not draw a tile twice per frame. // used to not draw a tile twice per frame.
private static int mDrawSerial = 0; private static int mDrawSerial = 0;
@ -597,10 +596,11 @@ public class GLRenderer implements GLSurfaceView.Renderer {
if (tile.lastDraw == mDrawSerial) if (tile.lastDraw == mDrawSerial)
return; return;
float div = scaleDiv(tile);
float[] mvp = mMVPMatrix; float[] mvp = mMVPMatrix;
MapPosition pos = mMapPosition; MapPosition pos = mMapPosition;
float div = FastMath.pow(tile.zoomLevel - pos.zoomLevel);
tile.lastDraw = mDrawSerial; tile.lastDraw = mDrawSerial;
setMatrix(mvp, tile, div, true); setMatrix(mvp, tile, div, true);
@ -611,13 +611,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
if (tile.layers == null) if (tile.layers == null)
return; return;
GLES20.glPolygonOffset(0, mDrawCount++); GLES20.glPolygonOffset(0, depthOffset(tile));
// seems there are not infinite offset units possible
// this should suffice for at least two rows, i.e.
// having not two neighbours with the same depth
if (mDrawCount == 20)
mDrawCount = 0;
GLES20.glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id); GLES20.glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id);

View File

@ -52,7 +52,7 @@ public final class MapTile extends JobTile {
/** /**
* pointer to access relatives in QuadTree * pointer to access relatives in QuadTree
*/ */
QuadTree rel; public QuadTree rel;
int lastDraw = 0; int lastDraw = 0;
@ -65,7 +65,7 @@ public final class MapTile extends JobTile {
final static int PROXY_GRAMPA = 1 << 5; final static int PROXY_GRAMPA = 1 << 5;
final static int PROXY_HOLDER = 1 << 6; final static int PROXY_HOLDER = 1 << 6;
byte proxies; public byte proxies;
// counting the tiles that use this tile as proxy // counting the tiles that use this tile as proxy
byte refs; byte refs;
@ -94,9 +94,6 @@ public final class MapTile extends JobTile {
if (locked > 1) if (locked > 1)
return; return;
//if (isReady || newData)
// return;
MapTile p = rel.parent.tile; MapTile p = rel.parent.tile;
if (p != null && (p.state != 0)) { if (p != null && (p.state != 0)) {

View File

@ -16,7 +16,7 @@ package org.oscim.renderer;
import android.util.Log; import android.util.Log;
class QuadTree { public class QuadTree {
private static String TAG = "QuadTree"; private static String TAG = "QuadTree";
// pointer to tile 0/0/0 // pointer to tile 0/0/0
@ -25,16 +25,16 @@ class QuadTree {
// parent pointer is used to link pool items // parent pointer is used to link pool items
private static QuadTree pool; private static QuadTree pool;
QuadTree parent; public QuadTree parent;
// .... x y // .... x y
// 0 => 0 0 // 0 => 0 0
// 1 => 1 0 // 1 => 1 0
// 2 => 0 1 // 2 => 0 1
// 3 => 1 1 // 3 => 1 1
final QuadTree[] child = new QuadTree[4]; public final QuadTree[] child = new QuadTree[4];
int refs = 0; int refs = 0;
byte id; byte id;
MapTile tile; public MapTile tile;
static void init() { static void init() {
pool = null; pool = null;

View File

@ -1,351 +0,0 @@
/*
* Copyright 2012 OpenScienceMap
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer.overlays;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import org.oscim.core.MapPosition;
import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.MapTile;
import org.oscim.renderer.TileManager;
import org.oscim.renderer.TileSet;
import org.oscim.renderer.layer.ExtrusionLayer;
import org.oscim.utils.GlUtils;
import org.oscim.view.MapView;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;
/**
* @author Hannes Janetzek
*/
public class BuildingOverlay2 extends RenderOverlay {
private final static String TAG = BuildingOverlay2.class.getName();
public BuildingOverlay2(MapView mapView) {
super(mapView);
}
private static int buildingProgram;
private static int hBuildingVertexPosition;
private static int hBuildingLightPosition;
private static int hBuildingMatrix;
private static int hBuildingColor;
private static int hBuildingMode;
private boolean initialized = false;
private int BUFFERSIZE = 65536 * 2;
private TileSet mTileSet;
private ShortBuffer mShortBuffer;
@Override
public synchronized void update(MapPosition curPos, boolean positionChanged,
boolean tilesChanged) {
mMapView.getMapViewPosition().getMapPosition(mMapPosition, null);
if (!initialized) {
initialized = true;
// Set up the program for rendering buildings
buildingProgram = GlUtils.createProgram(buildingVertexShader,
buildingFragmentShader);
if (buildingProgram == 0) {
Log.e("blah", "Could not create building program.");
return;
}
hBuildingMatrix = GLES20.glGetUniformLocation(buildingProgram, "u_mvp");
hBuildingColor = GLES20.glGetUniformLocation(buildingProgram, "u_color");
hBuildingMode = GLES20.glGetUniformLocation(buildingProgram, "u_mode");
hBuildingVertexPosition = GLES20.glGetAttribLocation(buildingProgram, "a_position");
hBuildingLightPosition = GLES20.glGetAttribLocation(buildingProgram, "a_light");
ByteBuffer buf = ByteBuffer.allocateDirect(BUFFERSIZE)
.order(ByteOrder.nativeOrder());
mShortBuffer = buf.asShortBuffer();
}
int ready = 0;
//if (curPos.zoomLevel < 17)
mTileSet = TileManager.getActiveTiles(mTileSet);
MapTile[] tiles = mTileSet.tiles;
for (int i = 0; i < mTileSet.cnt; i++) {
if (!tiles[i].isVisible || tiles[i].layers == null
|| tiles[i].layers.extrusionLayers == null)
continue;
ExtrusionLayer el = (ExtrusionLayer) tiles[i].layers.extrusionLayers;
if (el.ready && !el.compiled) {
el.compile(mShortBuffer);
GlUtils.checkGlError("...");
}
if (el.compiled)
ready++;
}
isReady = ready > 0;
}
// r: 0.815686275, 0.91372549
// g: 0.901960784
// b: 0.890196078
// sligthly differ adjacent faces to imrpove contrast
float mColor[] = { 0.71872549f, 0.701960784f, 0.690196078f, 0.7f };
float mColor2[] = { 0.71372549f, 0.701960784f, 0.695196078f, 0.7f };
float mRoofColor[] = { 0.895f, 0.89f, 0.88f, 0.9f };
boolean debug = false;
@Override
public synchronized void render(MapPosition pos, float[] mv, float[] proj) {
boolean first = true;
if (debug) {
MapTile[] tiles = mTileSet.tiles;
for (int i = 0; i < mTileSet.cnt; i++) {
if (!tiles[i].isVisible || tiles[i].layers == null
|| tiles[i].layers.extrusionLayers == null)
continue;
ExtrusionLayer el = (ExtrusionLayer) tiles[i].layers.extrusionLayers;
if (!el.compiled)
continue;
if (first) {
GLES20.glUseProgram(buildingProgram);
GLRenderer.enableVertexArrays(hBuildingVertexPosition, hBuildingLightPosition);
GLES20.glUniform1i(hBuildingMode, 0);
GLES20.glUniform4f(hBuildingColor, 0.6f, 0.6f, 0.6f, 0.8f);
first = false;
}
setMatrix(pos, mv, proj, tiles[i], 1);
GLES20.glUniformMatrix4fv(hBuildingMatrix, 1, false, mv, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, el.mIndicesBufferID);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, el.mVertexBufferID);
GLES20.glVertexAttribPointer(hBuildingVertexPosition, 3,
GLES20.GL_SHORT, false, 8, 0);
GLES20.glVertexAttribPointer(hBuildingLightPosition, 2,
GLES20.GL_UNSIGNED_BYTE, false, 8, 6);
GLES20.glUniform4f(hBuildingColor, 0.6f, 0.6f, 0.6f, 0.8f);
GLES20.glDrawElements(GLES20.GL_TRIANGLES,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]),
GLES20.GL_UNSIGNED_SHORT, 0);
GLES20.glUniform1i(hBuildingMode, 0);
GLES20.glUniform4f(hBuildingColor, 1.0f, 0.5f, 0.5f, 0.9f);
GLES20.glDrawElements(GLES20.GL_LINES, el.mIndiceCnt[3],
GLES20.GL_UNSIGNED_SHORT,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]) * 2);
}
return;
}
//int drawCount = 2;
// draw to depth buffer
MapTile[] tiles = mTileSet.tiles;
for (int i = 0; i < mTileSet.cnt; i++) {
if (!tiles[i].isVisible || tiles[i].layers == null
|| tiles[i].layers.extrusionLayers == null)
continue;
ExtrusionLayer el = (ExtrusionLayer) tiles[i].layers.extrusionLayers;
if (!el.compiled)
continue;
if (first) {
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glUseProgram(buildingProgram);
GLRenderer.enableVertexArrays(hBuildingVertexPosition, -1);
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glCullFace(GLES20.GL_FRONT);
GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthFunc(GLES20.GL_LESS);
GLES20.glDepthMask(true);
GLES20.glColorMask(false, false, false, false);
GLES20.glUniform1i(hBuildingMode, 0);
first = false;
}
GLES20.glPolygonOffset(1, 40);
// GLES20.glPolygonOffset(0, drawCount += 10);
// // seems there are not infinite offset units possible
// // this should suffice for at least two rows, i.e.
// // having not two neighbours with the same depth
// if (drawCount == 100)
// drawCount = 0;
setMatrix(pos, mv, proj, tiles[i], 1);
GLES20.glUniformMatrix4fv(hBuildingMatrix, 1, false, mv, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, el.mIndicesBufferID);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, el.mVertexBufferID);
GLES20.glVertexAttribPointer(hBuildingVertexPosition, 3,
GLES20.GL_SHORT, false, 8, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]),
GLES20.GL_UNSIGNED_SHORT, 0);
}
if (first)
return;
// enable color buffer, use depth mask
GLRenderer.enableVertexArrays(hBuildingVertexPosition, hBuildingLightPosition);
GLES20.glColorMask(true, true, true, true);
//GLES20.glDepthMask(false);
GLES20.glDepthFunc(GLES20.GL_LEQUAL);
//GLES20.glDepthFunc(GLES20.GL_EQUAL);
int drawCount = 40;
//GLES20.glEnbable(GLES20.GL_POLYGON_);
//GLES20.glPolygonOffset(0, -2);
for (int i = 0; i < mTileSet.cnt; i++) {
if (!tiles[i].isVisible || tiles[i].layers == null
|| tiles[i].layers.extrusionLayers == null)
continue;
ExtrusionLayer el = (ExtrusionLayer) tiles[i].layers.extrusionLayers;
if (!el.compiled)
continue;
GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL);
GLES20.glPolygonOffset(1, drawCount--);
if (drawCount == 20)
drawCount = 40;
setMatrix(pos, mv, proj, tiles[i], 1);
GLES20.glUniformMatrix4fv(hBuildingMatrix, 1, false, mv, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, el.mIndicesBufferID);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, el.mVertexBufferID);
//GLRenderer.enableVertexArrays(hBuildingVertexPosition, hBuildingLightPosition);
GLES20.glVertexAttribPointer(hBuildingVertexPosition, 3,
GLES20.GL_SHORT, false, 8, 0);
GLES20.glVertexAttribPointer(hBuildingLightPosition, 2,
GLES20.GL_UNSIGNED_BYTE, false, 8, 6);
// draw roof
GLES20.glUniform1i(hBuildingMode, 0);
//GLES20.glUniform4f(hBuildingColor, 0.81f, 0.8f, 0.8f, 0.9f);
GLES20.glUniform4fv(hBuildingColor, 1, mRoofColor, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, el.mIndiceCnt[2],
GLES20.GL_UNSIGNED_SHORT, (el.mIndiceCnt[0] + el.mIndiceCnt[1]) * 2);
// draw sides 1
//GLES20.glUniform4f(hBuildingColor, 0.8f, 0.8f, 0.8f, 1.0f);
//GLES20.glUniform4f(hBuildingColor, 0.9f, 0.905f, 0.9f, 1.0f);
GLES20.glUniform4fv(hBuildingColor, 1, mColor, 0);
GLES20.glUniform1i(hBuildingMode, 1);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, el.mIndiceCnt[0],
GLES20.GL_UNSIGNED_SHORT, 0);
// draw sides 2
//GLES20.glUniform4f(hBuildingColor, 0.9f, 0.9f, 0.905f, 1.0f);
GLES20.glUniform4fv(hBuildingColor, 1, mColor2, 0);
GLES20.glUniform1i(hBuildingMode, 2);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, el.mIndiceCnt[1],
GLES20.GL_UNSIGNED_SHORT, el.mIndiceCnt[0] * 2);
GLES20.glUniform1i(hBuildingMode, 0);
GLES20.glUniform4f(hBuildingColor, 0.75f, 0.75f, 0.75f, 1.0f);
GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL);
GLES20.glDrawElements(GLES20.GL_LINES, el.mIndiceCnt[3],
GLES20.GL_UNSIGNED_SHORT,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]) * 2);
// GlUtils.checkGlError(".2.");
}
if (!first) {
GLES20.glDisable(GLES20.GL_CULL_FACE);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
private static void setMatrix(MapPosition mapPosition, float[] matrix, float[] proj,
MapTile tile, float div) {
float x = (float) (tile.pixelX - mapPosition.x * div);
float y = (float) (tile.pixelY - mapPosition.y * div);
float scale = mapPosition.scale / div;
Matrix.setIdentityM(matrix, 0);
// translate relative to map center
matrix[12] = x * scale;
matrix[13] = y * scale;
// scale to tile to world coordinates
scale /= GLRenderer.COORD_MULTIPLIER;
matrix[0] = scale;
matrix[5] = scale;
matrix[10] = scale / 1000f;
Matrix.multiplyMM(matrix, 0, mapPosition.viewMatrix, 0, matrix, 0);
Matrix.multiplyMM(matrix, 0, proj, 0, matrix, 0);
}
final static String buildingVertexShader = ""
+ "precision mediump float;"
+ "uniform mat4 u_mvp;"
+ "uniform vec4 u_color;"
+ "uniform int u_mode;"
+ "uniform float u_scale;"
+ "attribute vec4 a_position;"
+ "attribute vec2 a_light;"
+ "varying vec4 color;"
+ "const float ff = 255.0;"
+ "void main() {"
+ " gl_Position = u_mvp * a_position;"
+ " if (u_mode == 0)"
// roof / depth pass
+ " color = u_color;"
+ " else if (u_mode == 1)"
// sides 1 - use 0xff00
+ " color = vec4(u_color.rgb * (a_light.y / ff), 0.8);"
+ " else"
// sides 2 - use 0x00ff
+ " color = vec4(u_color.rgb * (a_light.x / ff), 0.8);"
+ "}";
final static String buildingFragmentShader = ""
+ "precision lowp float;"
+ "varying vec4 color;"
+ "void main() {"
+ " gl_FragColor = color;"
+ "}";
}

View File

@ -0,0 +1,374 @@
/*
* Copyright 2012 OpenScienceMap
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer.overlays;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import org.oscim.core.MapPosition;
import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.MapTile;
import org.oscim.renderer.TileManager;
import org.oscim.renderer.TileSet;
import org.oscim.renderer.layer.ExtrusionLayer;
import org.oscim.utils.FastMath;
import org.oscim.utils.GlUtils;
import org.oscim.view.MapView;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;
/**
* @author Hannes Janetzek
*/
public class ExtrusionOverlay extends RenderOverlay {
private final static String TAG = ExtrusionOverlay.class.getName();
public ExtrusionOverlay(MapView mapView) {
super(mapView);
}
private static int extrusionProgram;
private static int hExtrusionVertexPosition;
private static int hExtrusionLightPosition;
private static int hExtrusionMatrix;
private static int hExtrusionColor;
private static int hExtrusionMode;
private boolean initialized = false;
// TODO sum up size used while filling layer only up to:
private int BUFFERSIZE = 65536 * 2;
private TileSet mTileSet;
private ShortBuffer mShortBuffer;
private MapTile[] mTiles;
private int mTileCnt;
@Override
public synchronized void update(MapPosition curPos, boolean positionChanged,
boolean tilesChanged) {
mMapView.getMapViewPosition().getMapPosition(mMapPosition, null);
if (!initialized) {
initialized = true;
// Set up the program for rendering extrusions
extrusionProgram = GlUtils.createProgram(extrusionVertexShader,
extrusionFragmentShader);
if (extrusionProgram == 0) {
Log.e(TAG, "Could not create extrusion shader program.");
return;
}
hExtrusionMatrix = GLES20.glGetUniformLocation(extrusionProgram, "u_mvp");
hExtrusionColor = GLES20.glGetUniformLocation(extrusionProgram, "u_color");
hExtrusionMode = GLES20.glGetUniformLocation(extrusionProgram, "u_mode");
hExtrusionVertexPosition = GLES20.glGetAttribLocation(extrusionProgram, "a_position");
hExtrusionLightPosition = GLES20.glGetAttribLocation(extrusionProgram, "a_light");
ByteBuffer buf = ByteBuffer.allocateDirect(BUFFERSIZE)
.order(ByteOrder.nativeOrder());
mShortBuffer = buf.asShortBuffer();
}
int ready = 0;
//if (curPos.zoomLevel < 17)
mTileSet = TileManager.getActiveTiles(mTileSet);
MapTile[] tiles = mTileSet.tiles;
// keep a list of tiles available for rendering
if (mTiles == null || mTiles.length != tiles.length)
mTiles = new MapTile[tiles.length];
ExtrusionLayer el;
if (curPos.zoomLevel >= 17) {
for (int i = 0; i < mTileSet.cnt; i++) {
if (!tiles[i].isVisible)
continue;
el = getLayer(tiles[i]);
if (el == null)
continue;
if (el.ready && !el.compiled) {
el.compile(mShortBuffer);
GlUtils.checkGlError("...");
}
if (el.compiled)
mTiles[ready++] = tiles[i];
}
} else if (curPos.zoomLevel == 16) {
for (int i = 0; i < mTileSet.cnt; i++) {
if (!tiles[i].isVisible)
continue;
MapTile t = tiles[i];
for (byte j = 0; j < 4; j++) {
if ((t.proxies & (1 << j)) != 0) {
MapTile c = t.rel.child[j].tile;
el = getLayer(c);
if (el == null || !el.compiled)
continue;
// TODO check overflow, even if very unlikely...
mTiles[ready++] = c;
}
}
}
}
mTileCnt = ready;
isReady = ready > 0;
}
private static ExtrusionLayer getLayer(MapTile t) {
if (t.layers != null && t.layers.extrusionLayers != null)
return (ExtrusionLayer) t.layers.extrusionLayers;
return null;
}
// sligthly differ adjacent faces to improve contrast
float mColor[] = { 0.71872549f, 0.701960784f, 0.690196078f, 0.7f };
float mColor2[] = { 0.71372549f, 0.701960784f, 0.695196078f, 0.7f };
float mRoofColor[] = { 0.895f, 0.89f, 0.88f, 0.9f };
boolean debug = false;
@Override
public synchronized void render(MapPosition pos, float[] mv, float[] proj) {
MapTile[] tiles = mTiles;
float div = FastMath.pow(tiles[0].zoomLevel - pos.zoomLevel);
int depthScale = 1;
if (debug) {
GLES20.glUseProgram(extrusionProgram);
GLRenderer
.enableVertexArrays(hExtrusionVertexPosition, hExtrusionLightPosition);
GLES20.glUniform1i(hExtrusionMode, 0);
GLES20.glUniform4f(hExtrusionColor, 0.6f, 0.6f, 0.6f, 0.8f);
for (int i = 0; i < mTileCnt; i++) {
ExtrusionLayer el = (ExtrusionLayer) tiles[i].layers.extrusionLayers;
setMatrix(pos, mv, proj, tiles[i], div);
GLES20.glUniformMatrix4fv(hExtrusionMatrix, 1, false, mv, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, el.mIndicesBufferID);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, el.mVertexBufferID);
GLES20.glVertexAttribPointer(hExtrusionVertexPosition, 3,
GLES20.GL_SHORT, false, 8, 0);
GLES20.glVertexAttribPointer(hExtrusionLightPosition, 2,
GLES20.GL_UNSIGNED_BYTE, false, 8, 6);
GLES20.glUniform4f(hExtrusionColor, 0.6f, 0.6f, 0.6f, 0.8f);
GLES20.glDrawElements(GLES20.GL_TRIANGLES,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]),
GLES20.GL_UNSIGNED_SHORT, 0);
GLES20.glUniform1i(hExtrusionMode, 0);
GLES20.glUniform4f(hExtrusionColor, 1.0f, 0.5f, 0.5f, 0.9f);
GLES20.glDrawElements(GLES20.GL_LINES, el.mIndiceCnt[3],
GLES20.GL_UNSIGNED_SHORT,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]) * 2);
// just a temporary reference!
tiles[i] = null;
}
return;
}
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glUseProgram(extrusionProgram);
GLRenderer.enableVertexArrays(hExtrusionVertexPosition, -1);
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glCullFace(GLES20.GL_FRONT);
GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthFunc(GLES20.GL_LESS);
GLES20.glDepthMask(true);
GLES20.glColorMask(false, false, false, false);
GLES20.glUniform1i(hExtrusionMode, 0);
// draw to depth buffer
for (int i = 0; i < mTileCnt; i++) {
ExtrusionLayer el = (ExtrusionLayer) tiles[i].layers.extrusionLayers;
GLES20.glPolygonOffset(depthScale, GLRenderer.depthOffset(tiles[i]));
setMatrix(pos, mv, proj, tiles[i], div);
GLES20.glUniformMatrix4fv(hExtrusionMatrix, 1, false, mv, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, el.mIndicesBufferID);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, el.mVertexBufferID);
GLES20.glVertexAttribPointer(hExtrusionVertexPosition, 3,
GLES20.GL_SHORT, false, 8, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]),
GLES20.GL_UNSIGNED_SHORT, 0);
}
// enable color buffer, use depth mask
GLRenderer.enableVertexArrays(hExtrusionVertexPosition, hExtrusionLightPosition);
GLES20.glColorMask(true, true, true, true);
GLES20.glDepthMask(false);
GLES20.glDepthFunc(GLES20.GL_EQUAL);
for (int i = 0; i < mTileCnt; i++) {
ExtrusionLayer el = (ExtrusionLayer) tiles[i].layers.extrusionLayers;
GLES20.glPolygonOffset(depthScale, GLRenderer.depthOffset(tiles[i]));
setMatrix(pos, mv, proj, tiles[i], div);
GLES20.glUniformMatrix4fv(hExtrusionMatrix, 1, false, mv, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, el.mIndicesBufferID);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, el.mVertexBufferID);
GLES20.glVertexAttribPointer(hExtrusionVertexPosition, 3,
GLES20.GL_SHORT, false, 8, 0);
GLES20.glVertexAttribPointer(hExtrusionLightPosition, 2,
GLES20.GL_UNSIGNED_BYTE, false, 8, 6);
// draw roof
GLES20.glUniform1i(hExtrusionMode, 0);
//GLES20.glUniform4f(hExtrusionColor, 0.81f, 0.8f, 0.8f, 0.9f);
GLES20.glUniform4fv(hExtrusionColor, 1, mRoofColor, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, el.mIndiceCnt[2],
GLES20.GL_UNSIGNED_SHORT, (el.mIndiceCnt[0] + el.mIndiceCnt[1]) * 2);
// draw sides 1
//GLES20.glUniform4f(hExtrusionColor, 0.8f, 0.8f, 0.8f, 1.0f);
//GLES20.glUniform4f(hExtrusionColor, 0.9f, 0.905f, 0.9f, 1.0f);
GLES20.glUniform4fv(hExtrusionColor, 1, mColor, 0);
GLES20.glUniform1i(hExtrusionMode, 1);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, el.mIndiceCnt[0],
GLES20.GL_UNSIGNED_SHORT, 0);
// draw sides 2
//GLES20.glUniform4f(hExtrusionColor, 0.9f, 0.9f, 0.905f, 1.0f);
GLES20.glUniform4fv(hExtrusionColor, 1, mColor2, 0);
GLES20.glUniform1i(hExtrusionMode, 2);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, el.mIndiceCnt[1],
GLES20.GL_UNSIGNED_SHORT, el.mIndiceCnt[0] * 2);
GLES20.glDepthFunc(GLES20.GL_LEQUAL);
GLES20.glUniform1i(hExtrusionMode, 0);
GLES20.glUniform4f(hExtrusionColor, 0.65f, 0.65f, 0.65f, 0.98f);
GLES20.glDrawElements(GLES20.GL_LINES, el.mIndiceCnt[3],
GLES20.GL_UNSIGNED_SHORT,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]) * 2);
GLES20.glDepthFunc(GLES20.GL_EQUAL);
// just a temporary reference!
tiles[i] = null;
}
GLES20.glDisable(GLES20.GL_CULL_FACE);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
}
private static void setMatrix(MapPosition mapPosition, float[] matrix, float[] proj,
MapTile tile, float div) {
float x = (float) (tile.pixelX - mapPosition.x * div);
float y = (float) (tile.pixelY - mapPosition.y * div);
float scale = mapPosition.scale / div;
Matrix.setIdentityM(matrix, 0);
// translate relative to map center
matrix[12] = x * scale;
matrix[13] = y * scale;
// scale to tile to world coordinates
scale /= GLRenderer.COORD_MULTIPLIER;
matrix[0] = scale;
matrix[5] = scale;
matrix[10] = scale / 1000f;
Matrix.multiplyMM(matrix, 0, mapPosition.viewMatrix, 0, matrix, 0);
Matrix.multiplyMM(matrix, 0, proj, 0, matrix, 0);
}
final static String extrusionVertexShader = ""
+ "precision mediump float;"
+ "uniform mat4 u_mvp;"
+ "uniform vec4 u_color;"
+ "uniform int u_mode;"
+ "attribute vec4 a_position;"
+ "attribute vec2 a_light;"
+ "varying vec4 color;"
+ "const float ff = 255.0;"
+ "void main() {"
+ " gl_Position = u_mvp * a_position;"
+ " if (u_mode == 0)"
// roof / depth pass
+ " color = u_color;"
+ " else if (u_mode == 1)"
// sides 1 - use 0xff00
+ " color = vec4(u_color.rgb * (a_light.y / ff), 0.8);"
+ " else"
// sides 2 - use 0x00ff
+ " color = vec4(u_color.rgb * (a_light.x / ff), 0.8);"
+ "}";
// final static String extrusionVertexAnimShader = ""
// + "precision mediump float;"
// + "uniform mat4 u_mvp;"
// + "uniform vec4 u_color;"
// + "uniform int u_mode;"
// + "uniform float u_adv;"
// + "attribute vec4 a_pos;"
// + "attribute vec2 a_light;"
// + "varying vec4 color;"
// + "const float ff = 255.0;"
// + "void main() {"
// + " gl_Position = u_mvp * vec4(a_pos.xy, a_pos.z * adv, 1.0);"
// + " if (u_mode == 0)"
// // roof / depth pass
// + " color = u_color;"
// + " else if (u_mode == 1)"
// // sides 1 - use 0xff00
// + " color = vec4(u_color.rgb * (a_light.y / ff), 0.8);"
// + " else"
// // sides 2 - use 0x00ff
// + " color = vec4(u_color.rgb * (a_light.x / ff), 0.8);"
// + "}";
final static String extrusionFragmentShader = ""
+ "precision lowp float;"
+ "varying vec4 color;"
+ "void main() {"
+ " gl_FragColor = color;"
+ "}";
}

View File

@ -45,7 +45,7 @@ import org.oscim.overlay.OverlayManager;
import org.oscim.renderer.GLRenderer; import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.GLView; import org.oscim.renderer.GLView;
import org.oscim.renderer.TileManager; import org.oscim.renderer.TileManager;
import org.oscim.renderer.overlays.BuildingOverlay2; import org.oscim.renderer.overlays.ExtrusionOverlay;
import org.oscim.theme.ExternalRenderTheme; import org.oscim.theme.ExternalRenderTheme;
import org.oscim.theme.InternalRenderTheme; import org.oscim.theme.InternalRenderTheme;
import org.oscim.theme.RenderTheme; import org.oscim.theme.RenderTheme;
@ -185,7 +185,7 @@ public class MapView extends RelativeLayout {
enableRotation = true; enableRotation = true;
//mOverlayManager.add(new GenericOverlay(this, new GridOverlay(this))); //mOverlayManager.add(new GenericOverlay(this, new GridOverlay(this)));
mOverlayManager.add(new GenericOverlay(this, new BuildingOverlay2(this))); mOverlayManager.add(new GenericOverlay(this, new ExtrusionOverlay(this)));
mOverlayManager.add(new LabelingOverlay(this)); mOverlayManager.add(new LabelingOverlay(this));
// mOverlayManager.add(new GenericOverlay(this, new TestOverlay(this))); // mOverlayManager.add(new GenericOverlay(this, new TestOverlay(this)));