471 lines
14 KiB
Java
471 lines
14 KiB
Java
/*
|
|
* 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.generator.JobTile;
|
|
import org.oscim.renderer.GLRenderer;
|
|
import org.oscim.renderer.GLRenderer.Matrices;
|
|
import org.oscim.renderer.GLState;
|
|
import org.oscim.renderer.MapTile;
|
|
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 = new int[2];
|
|
private static int[] hExtrusionVertexPosition = new int[2];
|
|
private static int[] hExtrusionLightPosition = new int[2];
|
|
private static int[] hExtrusionMatrix = new int[2];
|
|
private static int[] hExtrusionColor = new int[2];
|
|
private static int[] hExtrusionAlpha = new int[2];
|
|
private static int[] hExtrusionMode = new int[2];
|
|
|
|
private boolean initialized = false;
|
|
|
|
// FIXME sum up size used while filling layer only up to:
|
|
private final int BUFFERSIZE = 65536 * 2;
|
|
private TileSet mTileSet;
|
|
private ShortBuffer mShortBuffer;
|
|
private MapTile[] mTiles;
|
|
private int mTileCnt;
|
|
|
|
@Override
|
|
public void update(MapPosition curPos, boolean positionChanged,
|
|
boolean tilesChanged) {
|
|
|
|
mMapView.getMapViewPosition().getMapPosition(mMapPosition);
|
|
|
|
if (!initialized) {
|
|
initialized = true;
|
|
|
|
for (int i = 1; i < 2; i++) {
|
|
// Set up the program for rendering extrusions
|
|
extrusionProgram[i] = GlUtils.createProgram(extrusionVertexShader[i],
|
|
extrusionFragmentShader);
|
|
if (extrusionProgram[i] == 0) {
|
|
Log.e(TAG, "Could not create extrusion shader program. " + i);
|
|
return;
|
|
}
|
|
hExtrusionMatrix[i] = GLES20.glGetUniformLocation(extrusionProgram[i], "u_mvp");
|
|
hExtrusionColor[i] = GLES20.glGetUniformLocation(extrusionProgram[i], "u_color");
|
|
hExtrusionAlpha[i] = GLES20.glGetUniformLocation(extrusionProgram[i], "u_alpha");
|
|
hExtrusionMode[i] = GLES20.glGetUniformLocation(extrusionProgram[i], "u_mode");
|
|
hExtrusionVertexPosition[i] = GLES20.glGetAttribLocation(extrusionProgram[i],
|
|
"a_pos");
|
|
hExtrusionLightPosition[i] = GLES20.glGetAttribLocation(extrusionProgram[i],
|
|
"a_light");
|
|
}
|
|
|
|
ByteBuffer buf = ByteBuffer.allocateDirect(BUFFERSIZE)
|
|
.order(ByteOrder.nativeOrder());
|
|
|
|
mShortBuffer = buf.asShortBuffer();
|
|
}
|
|
|
|
int ready = 0;
|
|
mTileSet = mMapView.getTileManager().getActiveTiles(mTileSet);
|
|
MapTile[] tiles = mTileSet.tiles;
|
|
// FIXME just release tiles in this case
|
|
if (mAlpha == 0 || curPos.zoomLevel < 16) {
|
|
isReady = false;
|
|
return;
|
|
}
|
|
|
|
// keep a list of tiles available for rendering
|
|
if (mTiles == null || mTiles.length < mTileSet.cnt * 4)
|
|
mTiles = new MapTile[mTileSet.cnt * 4];
|
|
|
|
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.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;
|
|
|
|
mTiles[ready++] = c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mTileCnt = ready;
|
|
isReady = ready > 0;
|
|
}
|
|
|
|
private static ExtrusionLayer getLayer(MapTile t) {
|
|
if (t.layers != null && t.layers.extrusionLayers != null
|
|
&& t.state == JobTile.STATE_READY)
|
|
return (ExtrusionLayer) t.layers.extrusionLayers;
|
|
return null;
|
|
}
|
|
|
|
private final boolean debug = false;
|
|
//private final float[] mVPMatrix = new float[16];
|
|
|
|
@Override
|
|
public void render(MapPosition pos, Matrices m) {
|
|
// TODO one could render in one pass to texture and then draw the texture
|
|
// with alpha... might be faster.
|
|
|
|
//Matrix.multiplyMM(mVPMatrix, 0, proj, 0, pos.viewMatrix, 0);
|
|
//proj = mVPMatrix;
|
|
|
|
MapTile[] tiles = mTiles;
|
|
|
|
float div = FastMath.pow(tiles[0].zoomLevel - pos.zoomLevel);
|
|
|
|
int shaderMode = 1;
|
|
int uExtAlpha = hExtrusionAlpha[shaderMode];
|
|
int uExtColor = hExtrusionColor[shaderMode];
|
|
int uExtVertexPosition = hExtrusionVertexPosition[shaderMode];
|
|
int uExtLightPosition = hExtrusionLightPosition[shaderMode];
|
|
int uExtMatrix = hExtrusionMatrix[shaderMode];
|
|
int uExtMode = hExtrusionMode[shaderMode];
|
|
|
|
if (debug) {
|
|
GLState.useProgram(extrusionProgram[shaderMode]);
|
|
|
|
GLState.enableVertexArrays(uExtVertexPosition, uExtLightPosition);
|
|
GLES20.glUniform1i(uExtMode, 0);
|
|
GLES20.glUniform4fv(uExtColor, 4, mColor, 0);
|
|
|
|
GLState.test(false, false);
|
|
|
|
for (int i = 0; i < mTileCnt; i++) {
|
|
ExtrusionLayer el = (ExtrusionLayer) tiles[i].layers.extrusionLayers;
|
|
|
|
setMatrix(pos, m, tiles[i], div, 0);
|
|
GLES20.glUniformMatrix4fv(uExtMatrix, 1, false, m.mvp, 0);
|
|
|
|
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, el.mIndicesBufferID);
|
|
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, el.mVertexBufferID);
|
|
|
|
GLES20.glVertexAttribPointer(uExtVertexPosition, 3,
|
|
GLES20.GL_SHORT, false, 8, 0);
|
|
|
|
GLES20.glVertexAttribPointer(uExtLightPosition, 2,
|
|
GLES20.GL_UNSIGNED_BYTE, false, 8, 6);
|
|
|
|
GLES20.glDrawElements(GLES20.GL_TRIANGLES,
|
|
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]),
|
|
GLES20.GL_UNSIGNED_SHORT, 0);
|
|
|
|
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.glDepthMask(true);
|
|
//GLES20.glStencilMask(0xff);
|
|
//GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_STENCIL_BUFFER_BIT);
|
|
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
|
|
|
|
GLState.test(true, false);
|
|
|
|
GLState.useProgram(extrusionProgram[shaderMode]);
|
|
GLState.enableVertexArrays(uExtVertexPosition, -1);
|
|
if (pos.scale < 2) {
|
|
// chances are high that one moves through a building
|
|
// with scale > 2 also draw back sides in this case.
|
|
GLES20.glEnable(GLES20.GL_CULL_FACE);
|
|
GLES20.glCullFace(GLES20.GL_FRONT);
|
|
}
|
|
GLES20.glDepthFunc(GLES20.GL_LESS);
|
|
GLES20.glColorMask(false, false, false, false);
|
|
GLES20.glUniform1i(uExtMode, 0);
|
|
GLES20.glUniform4fv(uExtColor, 4, mColor, 0);
|
|
GLES20.glUniform1f(uExtAlpha, mAlpha);
|
|
|
|
// draw to depth buffer
|
|
for (int i = 0; i < mTileCnt; i++) {
|
|
MapTile t = tiles[i];
|
|
ExtrusionLayer el = (ExtrusionLayer) t.layers.extrusionLayers;
|
|
int d = GLRenderer.depthOffset(t) * 10;
|
|
setMatrix(pos, m, t, div, d);
|
|
GLES20.glUniformMatrix4fv(uExtMatrix, 1, false, m.mvp, 0);
|
|
|
|
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, el.mIndicesBufferID);
|
|
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, el.mVertexBufferID);
|
|
|
|
GLES20.glVertexAttribPointer(uExtVertexPosition, 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
|
|
GLState.enableVertexArrays(uExtVertexPosition, uExtLightPosition);
|
|
GLES20.glColorMask(true, true, true, true);
|
|
GLES20.glDepthMask(false);
|
|
GLState.blend(true);
|
|
|
|
for (int i = 0; i < mTileCnt; i++) {
|
|
MapTile t = tiles[i];
|
|
ExtrusionLayer el = (ExtrusionLayer) t.layers.extrusionLayers;
|
|
|
|
GLES20.glDepthFunc(GLES20.GL_EQUAL);
|
|
int d = GLRenderer.depthOffset(t) * 10;
|
|
setMatrix(pos, m, t, div, d);
|
|
GLES20.glUniformMatrix4fv(uExtMatrix, 1, false, m.mvp, 0);
|
|
|
|
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, el.mIndicesBufferID);
|
|
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, el.mVertexBufferID);
|
|
|
|
GLES20.glVertexAttribPointer(uExtVertexPosition, 3,
|
|
GLES20.GL_SHORT, false, 8, 0);
|
|
|
|
GLES20.glVertexAttribPointer(uExtLightPosition, 2,
|
|
GLES20.GL_UNSIGNED_BYTE, false, 8, 6);
|
|
|
|
// draw roof
|
|
GLES20.glUniform1i(uExtMode, 0);
|
|
GLES20.glDrawElements(GLES20.GL_TRIANGLES, el.mIndiceCnt[2],
|
|
GLES20.GL_UNSIGNED_SHORT, (el.mIndiceCnt[0] + el.mIndiceCnt[1]) * 2);
|
|
|
|
// draw sides 1
|
|
GLES20.glUniform1i(uExtMode, 1);
|
|
GLES20.glDrawElements(GLES20.GL_TRIANGLES, el.mIndiceCnt[0],
|
|
GLES20.GL_UNSIGNED_SHORT, 0);
|
|
|
|
// draw sides 2
|
|
GLES20.glUniform1i(uExtMode, 2);
|
|
GLES20.glDrawElements(GLES20.GL_TRIANGLES, el.mIndiceCnt[1],
|
|
GLES20.GL_UNSIGNED_SHORT, el.mIndiceCnt[0] * 2);
|
|
|
|
// drawing gl_lines with the same coordinates does not result in
|
|
// same depth values as polygons, so add offset and draw gl_lequal:
|
|
GLES20.glDepthFunc(GLES20.GL_LEQUAL);
|
|
GlUtils.addOffsetM(m.mvp, 100);
|
|
GLES20.glUniformMatrix4fv(uExtMatrix, 1, false, m.mvp, 0);
|
|
|
|
GLES20.glUniform1i(uExtMode, 3);
|
|
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;
|
|
}
|
|
|
|
if (pos.scale < 2)
|
|
GLES20.glDisable(GLES20.GL_CULL_FACE);
|
|
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
}
|
|
|
|
private static void setMatrix(MapPosition mapPosition, Matrices m,
|
|
MapTile tile, float div, int delta) {
|
|
|
|
float x = (float) (tile.pixelX - mapPosition.x * div);
|
|
float y = (float) (tile.pixelY - mapPosition.y * div);
|
|
float scale = mapPosition.scale / div;
|
|
|
|
GlUtils.setTileMatrix(m.mvp, x, y, scale);
|
|
// scale height
|
|
m.mvp[10] = scale / (1000f * GLRenderer.COORD_MULTIPLIER);
|
|
|
|
Matrix.multiplyMM(m.mvp, 0, m.viewproj, 0, m.mvp, 0);
|
|
|
|
GlUtils.addOffsetM(m.mvp, delta);
|
|
}
|
|
|
|
private final float _a = 0.86f;
|
|
private final float _r = 0xe9;
|
|
private final float _g = 0xe8;
|
|
private final float _b = 0xe6;
|
|
private final float _o = 55;
|
|
private final float _s = 20;
|
|
private final float _l = 14;
|
|
private float mAlpha = 1;
|
|
private final float[] mColor = {
|
|
// roof color
|
|
_a * ((_r + _l + 1) / 255),
|
|
_a * ((_g + _l + 1) / 255),
|
|
_a * ((_b + _l) / 255),
|
|
_a,
|
|
// sligthly differ adjacent side
|
|
// faces to improve contrast
|
|
_a * ((_r - _s) / 255 + 0.01f),
|
|
_a * ((_g - _s) / 255 + 0.01f),
|
|
_a * ((_b - _s) / 255),
|
|
_a,
|
|
_a * ((_r - _s) / 255),
|
|
_a * ((_g - _s) / 255),
|
|
_a * ((_b - _s) / 255),
|
|
_a,
|
|
// roof outline
|
|
(_r - _o) / 255,
|
|
(_g - _o) / 255,
|
|
(_b - _o) / 255,
|
|
// 1, 0, 0,
|
|
1.0f,
|
|
};
|
|
|
|
final static String[] extrusionVertexShader = {
|
|
"precision mediump float;"
|
|
+ "uniform mat4 u_mvp;"
|
|
+ "uniform vec4 u_color[4];"
|
|
+ "uniform int u_mode;"
|
|
+ "uniform float u_alpha;"
|
|
+ "attribute vec4 a_pos;"
|
|
+ "attribute vec2 a_light;"
|
|
+ "varying vec4 color;"
|
|
+ "const float ff = 255.0;"
|
|
+ "float c_alpha = 0.8;"
|
|
+ "void main() {"
|
|
+ " gl_Position = u_mvp * a_pos;"
|
|
+ " if (u_mode == 0)"
|
|
// roof / depth pass
|
|
+ " color = u_color[0];"
|
|
+ " else {"
|
|
// decrease contrast with distance
|
|
+ " float z = (0.96 + gl_Position.z * 0.04);"
|
|
+ " if (u_mode == 1){"
|
|
// sides 1 - use 0xff00
|
|
// scale direction to -0.5<>0.5
|
|
+ " float dir = abs(a_light.y / ff - 0.5);"
|
|
+ " color = u_color[1] * z;"
|
|
+ " color.rgb *= (0.7 + dir * 0.4);"
|
|
+ " } else if (u_mode == 2){"
|
|
// sides 2 - use 0x00ff
|
|
+ " float dir = abs(a_light.x / ff - 0.5);"
|
|
+ " color = u_color[2] * z;"
|
|
+ " color.rgb *= (0.7 + dir * 0.4);"
|
|
+ " } else"
|
|
// outline
|
|
+ " color = u_color[3] * z;"
|
|
+ "}}",
|
|
|
|
"precision mediump float;"
|
|
+ "uniform mat4 u_mvp;"
|
|
+ "uniform vec4 u_color[4];"
|
|
+ "uniform int u_mode;"
|
|
+ "uniform float u_alpha;"
|
|
+ "attribute vec4 a_pos;"
|
|
+ "attribute vec2 a_light;"
|
|
+ "varying vec4 color;"
|
|
//+ "varying float z;"
|
|
+ "const float ff = 255.0;"
|
|
+ "void main() {"
|
|
// change height by u_alpha
|
|
+ " gl_Position = u_mvp * vec4(a_pos.xy, a_pos.z * u_alpha, 1.0);"
|
|
//+ " z = gl_Position.z;"
|
|
+ " if (u_mode == 0)"
|
|
// roof / depth pass
|
|
+ " color = u_color[0];"
|
|
+ " else {"
|
|
// decrease contrast with distance
|
|
+ " if (u_mode == 1){"
|
|
// sides 1 - use 0xff00
|
|
// scale direction to -0.5<>0.5
|
|
//+ " float dir = abs(a_light.y / ff - 0.5);"
|
|
+ " float dir = a_light.y / ff;"
|
|
+ " float z = (0.98 + gl_Position.z * 0.02);"
|
|
+ " color = u_color[1];"
|
|
+ " color.rgb *= (0.88 + dir * 0.12) * z;"
|
|
+ " } else if (u_mode == 2){"
|
|
// sides 2 - use 0x00ff
|
|
//+ " float dir = abs(a_light.x / ff - 0.5);"
|
|
+ " float dir = a_light.x / ff;"
|
|
+ " float z = (0.98 + gl_Position.z * 0.02);"
|
|
+ " color = u_color[2] * z;"
|
|
+ " color.rgb *= (0.88 + dir * 0.12) * z;"
|
|
+ " } else {"
|
|
// outline
|
|
+ " float z = (0.8 - gl_Position.z * 0.2);"
|
|
+ " color = u_color[3] * z;"
|
|
+ "}}}"
|
|
};
|
|
|
|
final static String extrusionFragmentShader = ""
|
|
+ "precision mediump float;"
|
|
+ "varying vec4 color;"
|
|
+ "void main() {"
|
|
+ " gl_FragColor = color;"
|
|
+ "}";
|
|
|
|
final static String extrusionFragmentShaderZ = ""
|
|
+ "precision highp float;"
|
|
+ "uniform vec4 u_color;"
|
|
+ "varying float z;"
|
|
+ "void main() {"
|
|
+ "if (z < 0.0)"
|
|
+ " gl_FragColor = vec4(z * -1.0, 0.0, 0.0, 1.0);"
|
|
+ "else"
|
|
+ " gl_FragColor = vec4(0.0, 0.0, z, 1.0);"
|
|
+ "}";
|
|
|
|
public void setAlpha(float a) {
|
|
mAlpha = a;
|
|
}
|
|
|
|
@Override
|
|
public void compile() {
|
|
// TODO Auto-generated method stub
|
|
|
|
}
|
|
}
|