vtm/src/org/oscim/renderer/overlays/BuildingOverlay2.java
Hannes Janetzek 35923938db use glCullFace GL_FRONT to avoid having to flip triangles
triangle-jni: some loop optimizations
2013-10-09 01:52:20 +02:00

352 lines
11 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.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;"
+ "}";
}