s3db: render meshes with ExtrusionRenderer

This commit is contained in:
Hannes Janetzek 2014-03-21 01:53:17 +01:00
parent f929c15e2d
commit a18d20a916
2 changed files with 345 additions and 88 deletions

View File

@ -37,12 +37,14 @@ public class ExtrusionRenderer extends LayerRenderer {
static final Logger log = LoggerFactory.getLogger(ExtrusionRenderer.class);
private final TileRenderer mTileLayer;
private final int mTileZoom;
protected float mAlpha = 1;
public ExtrusionRenderer(TileRenderer tileRenderLayer) {
public ExtrusionRenderer(TileRenderer tileRenderLayer, int tileZoom) {
mTileLayer = tileRenderLayer;
mTileSet = new TileSet();
mTileZoom = tileZoom;
}
private static int[] shaderProgram = new int[2];
@ -90,7 +92,7 @@ public class ExtrusionRenderer extends LayerRenderer {
}
@Override
protected void update(GLViewport v) {
public void update(GLViewport v) {
if (!initialized && !initShader())
return;
@ -98,17 +100,17 @@ public class ExtrusionRenderer extends LayerRenderer {
if (shaderProgram[0] == 0)
return;
if (mAlpha == 0 || v.pos.zoomLevel < 16) {
if (mAlpha == 0 || v.pos.zoomLevel < mTileZoom) {
setReady(false);
return;
}
if (mUpdateColors) {
synchronized (this) {
System.arraycopy(mNewColors, 0, mColor, 0, 16);
mUpdateColors = false;
}
}
// if (mUpdateColors) {
// synchronized (this) {
// System.arraycopy(mNewColors, 0, mColor, 0, 16);
// mUpdateColors = false;
// }
// }
int activeTiles = 0;
mTileLayer.getVisibleTiles(mTileSet);
@ -127,7 +129,7 @@ public class ExtrusionRenderer extends LayerRenderer {
int zoom = tiles[0].zoomLevel;
ExtrusionLayer el;
if (zoom == 17) {
if (zoom == mTileZoom) {
for (int i = 0; i < mTileSet.cnt; i++) {
el = getLayer(tiles[i]);
if (el == null)
@ -142,7 +144,31 @@ public class ExtrusionRenderer extends LayerRenderer {
if (el.compiled)
mTiles[activeTiles++] = tiles[i];
}
} else if (zoom == 16) {
} else if (zoom == mTileZoom + 1) {
O: for (int i = 0; i < mTileSet.cnt; i++) {
MapTile t = tiles[i].node.parent();
if (t == null)
continue;
for (MapTile c : mTiles)
if (c == t)
continue O;
el = getLayer(t);
if (el == null)
continue;
if (!el.compiled) {
int numShorts = el.mNumVertices * 8;
el.compile(MapRenderer.getShortBuffer(numShorts));
GLUtils.checkGlError("...");
}
if (el.compiled)
mTiles[activeTiles++] = t;
}
} else if (zoom == mTileZoom - 1) {
// check if proxy children are ready
for (int i = 0; i < mTileSet.cnt; i++) {
MapTile t = tiles[i];
@ -161,7 +187,26 @@ public class ExtrusionRenderer extends LayerRenderer {
}
}
// // check if proxy children are ready
// for (int i = 0; i < mTileSet.cnt; i++) {
// MapTile t = tiles[i];
// for (byte j = 0; j < 4; j++) {
// if (!t.hasProxy(1 << j))
// continue;
//
// MapTile c = t.rel.get(j);
// el = getLayer(c);
//
// if (el == null || !el.compiled)
// continue;
//
// mTiles[activeTiles++] = c;
// }
// }
// }
mTileCnt = activeTiles;
//log.debug("" + activeTiles + " " + zoom);
if (activeTiles > 0)
setReady(true);
@ -179,8 +224,10 @@ public class ExtrusionRenderer extends LayerRenderer {
private final boolean debug = false;
private final boolean drawAlpha = false;
@Override
protected void render(GLViewport v) {
public void render(GLViewport v) {
// TODO one could render in one pass to texture and then draw the texture
// with alpha... might be faster and would allow postprocessing outlines.
@ -199,9 +246,10 @@ public class ExtrusionRenderer extends LayerRenderer {
GLState.enableVertexArrays(uExtVertexPosition, uExtLightPosition);
GL.glUniform1i(uExtMode, 0);
GLUtils.glUniform4fv(uExtColor, 4, mColor);
GL.glUniform1f(uExtAlpha, 1);
GLState.test(false, false);
GLState.blend(true);
for (int i = 0; i < mTileCnt; i++) {
ExtrusionLayer el = tiles[i].getLayers().getExtrusionLayers();
@ -240,41 +288,46 @@ public class ExtrusionRenderer extends LayerRenderer {
GLState.useProgram(shaderProgram[SHADER]);
GLState.enableVertexArrays(uExtVertexPosition, -1);
if (v.pos.scale < (1 << 18)) {
// chances are high that one moves through a building
// with scale > 2 also draw back sides in this case.
GL.glEnable(GL20.GL_CULL_FACE);
}
GLState.blend(false);
GL.glEnable(GL20.GL_CULL_FACE);
GL.glDepthFunc(GL20.GL_LESS);
GL.glColorMask(false, false, false, false);
GL.glUniform1i(uExtMode, -1);
//GLUtils.glUniform4fv(uExtColor, 4, mColor);
GL.glUniform1f(uExtAlpha, mAlpha);
// draw to depth buffer
for (int i = 0; i < mTileCnt; i++) {
MapTile t = tiles[i];
ExtrusionLayer el = t.getLayers().getExtrusionLayers();
int d = MapTile.depthOffset(t) * 10;
setMatrix(v, t, d);
v.mvp.setAsUniform(uExtMatrix);
//GL.glUniform1f(uExtAlpha, mAlpha);
GL.glUniform1f(uExtAlpha, 1);
el.vboIndices.bind();
el.vboVertices.bind();
if (drawAlpha) {
GL.glVertexAttribPointer(uExtVertexPosition, 3,
GL20.GL_SHORT, false, 8, 0);
GL.glColorMask(false, false, false, false);
GL.glUniform1i(uExtMode, -1);
//GLUtils.glUniform4fv(uExtColor, 4, mColor);
GL.glDrawElements(GL20.GL_TRIANGLES,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]),
GL20.GL_UNSIGNED_SHORT, 0);
// draw to depth buffer
for (int i = 0; i < mTileCnt; i++) {
MapTile t = tiles[i];
ExtrusionLayer el = t.getLayers().getExtrusionLayers();
int d = MapTile.depthOffset(t) * 10;
setMatrix(v, t, d);
v.mvp.setAsUniform(uExtMatrix);
el.vboIndices.bind();
el.vboVertices.bind();
GL.glVertexAttribPointer(uExtVertexPosition, 3,
GL20.GL_SHORT, false, 8, 0);
GL.glDrawElements(GL20.GL_TRIANGLES,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]),
GL20.GL_UNSIGNED_SHORT, 0);
}
GL.glColorMask(true, true, true, true);
GL.glDepthMask(false);
GLState.blend(true);
}
// enable color buffer, use depth mask
GLState.enableVertexArrays(uExtVertexPosition, uExtLightPosition);
GL.glColorMask(true, true, true, true);
GL.glDepthMask(false);
GLState.blend(true);
float[] currentColor = null;
@ -290,8 +343,12 @@ public class ExtrusionRenderer extends LayerRenderer {
GLUtils.glUniform4fv(uExtColor, 4, currentColor);
}
GL.glDepthFunc(GL20.GL_EQUAL);
int d = MapTile.depthOffset(t) * 10;
int d = 1;
if (drawAlpha) {
GL.glDepthFunc(GL20.GL_EQUAL);
d = MapTile.depthOffset(t) * 10;
}
setMatrix(v, t, d);
v.mvp.setAsUniform(uExtMatrix);
@ -304,62 +361,75 @@ public class ExtrusionRenderer extends LayerRenderer {
GL.glVertexAttribPointer(uExtLightPosition, 2,
GL20.GL_UNSIGNED_BYTE, false, 8, 6);
// draw roof
GL.glUniform1i(uExtMode, 0);
GL.glDrawElements(GL20.GL_TRIANGLES, el.mIndiceCnt[2],
GL20.GL_UNSIGNED_SHORT, (el.mIndiceCnt[0] + el.mIndiceCnt[1]) * 2);
// draw extruded outlines
if (el.mIndiceCnt[0] > 0) {
// draw roof
GL.glUniform1i(uExtMode, 0);
GL.glDrawElements(GL20.GL_TRIANGLES, el.mIndiceCnt[2],
GL20.GL_UNSIGNED_SHORT,
(el.mIndiceCnt[0] + el.mIndiceCnt[1]) * 2);
// draw sides 1
GL.glUniform1i(uExtMode, 1);
GL.glDrawElements(GL20.GL_TRIANGLES, el.mIndiceCnt[0],
GL20.GL_UNSIGNED_SHORT, 0);
// draw sides 1
GL.glUniform1i(uExtMode, 1);
GL.glDrawElements(GL20.GL_TRIANGLES, el.mIndiceCnt[0],
GL20.GL_UNSIGNED_SHORT, 0);
// draw sides 2
GL.glUniform1i(uExtMode, 2);
GL.glDrawElements(GL20.GL_TRIANGLES, el.mIndiceCnt[1],
GL20.GL_UNSIGNED_SHORT, el.mIndiceCnt[0] * 2);
// draw sides 2
GL.glUniform1i(uExtMode, 2);
GL.glDrawElements(GL20.GL_TRIANGLES, el.mIndiceCnt[1],
GL20.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:
GL.glDepthFunc(GL20.GL_LEQUAL);
if (drawAlpha) {
// drawing gl_lines with the same coordinates does not result in
// same depth values as polygons, so add offset and draw gl_lequal:
GL.glDepthFunc(GL20.GL_LEQUAL);
}
v.mvp.addDepthOffset(100);
v.mvp.setAsUniform(uExtMatrix);
v.mvp.addDepthOffset(100);
v.mvp.setAsUniform(uExtMatrix);
GL.glUniform1i(uExtMode, 3);
GL.glDrawElements(GL20.GL_LINES, el.mIndiceCnt[3],
GL20.GL_UNSIGNED_SHORT,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]) * 2);
GL.glUniform1i(uExtMode, 3);
GL.glDrawElements(GL20.GL_LINES, el.mIndiceCnt[3],
GL20.GL_UNSIGNED_SHORT,
(el.mIndiceCnt[0] + el.mIndiceCnt[1] + el.mIndiceCnt[2]) * 2);
}
// draw triangle meshes
if (el.mIndiceCnt[4] > 0) {
int offset = (el.mIndiceCnt[0] + el.mIndiceCnt[1]
+ el.mIndiceCnt[2] + el.mIndiceCnt[3]) * 2;
GL.glUniform1i(uExtMode, 4);
GL.glDrawElements(GL20.GL_TRIANGLES, el.mIndiceCnt[4],
GL20.GL_UNSIGNED_SHORT, offset);
}
// just a temporary reference!
tiles[i] = null;
}
if (v.pos.scale < (1 << 18))
GL.glDisable(GL20.GL_CULL_FACE);
GL.glDepthMask(false);
GL.glDisable(GL20.GL_CULL_FACE);
GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
mTileLayer.releaseTiles(mTileSet);
}
private static void setMatrix(GLViewport m,
MapTile tile, int delta) {
private static void setMatrix(GLViewport v, MapTile tile, int delta) {
int z = tile.zoomLevel;
double curScale = Tile.SIZE * m.pos.scale;
float scale = (float) (m.pos.scale / (1 << z));
double curScale = Tile.SIZE * v.pos.scale;
float scale = (float) (v.pos.scale / (1 << z));
float x = (float) ((tile.x - m.pos.x) * curScale);
float y = (float) ((tile.y - m.pos.y) * curScale);
m.mvp.setTransScale(x, y, scale / MapRenderer.COORD_SCALE);
float x = (float) ((tile.x - v.pos.x) * curScale);
float y = (float) ((tile.y - v.pos.y) * curScale);
v.mvp.setTransScale(x, y, scale / MapRenderer.COORD_SCALE);
// scale height
m.mvp.setValue(10, scale / 10);
// scale height ???
v.mvp.setValue(10, scale / 10);
m.mvp.multiplyLhs(m.viewproj);
v.mvp.multiplyLhs(v.viewproj);
m.mvp.addDepthOffset(delta);
v.mvp.addDepthOffset(delta);
}
public synchronized void setColors(float[] colors) {
@ -394,7 +464,7 @@ public class ExtrusionRenderer extends LayerRenderer {
mNewColors[15] = Color.aToFloat(lines);
}
private boolean mUpdateColors;
private volatile boolean mUpdateColors;
private final float[] mNewColors = new float[16];
private final float _a = 0.88f;
@ -404,6 +474,7 @@ public class ExtrusionRenderer extends LayerRenderer {
private final float _o = 20;
private final float _s = 4;
private final float _l = 0;
private final float[] mColor = {
// roof color
_a * ((_r + _l) / 255),
@ -445,10 +516,10 @@ public class ExtrusionRenderer extends LayerRenderer {
+ " if (u_mode == -1) ;"
// roof / depth pass
//+ " color = u_color[0] * u_alpha;"
+ " else if (u_mode == 0)"
+ " else if (u_mode == 0){"
// roof / depth pass
+ " color = u_color[0] * u_alpha;"
+ " else {"
+ " color = u_color[0] * u_alpha;"
+ " } else {"
// decrease contrast with distance
+ " if (u_mode == 1){"
// sides 1 - use 0xff00
@ -467,10 +538,22 @@ public class ExtrusionRenderer extends LayerRenderer {
+ " color = u_color[2];"
+ " color.rgb *= (0.8 + dir * 0.2) * z * h;"
+ " color *= u_alpha;"
+ " } else {"
+ " } else if (u_mode == 3) {"
// outline
+ " float z = (0.98 - gl_Position.z * 0.02);"
+ " color = u_color[3] * z;"
+ " } else {"
// normalize face x/y direction
+ " vec2 n = (a_light / 255.0 - 0.5) * 2.0;"
// normal points up or down (1,-1)
+ " float dir = 1.0 - (2.0 * abs(mod(a_light.x,2.0)));"
// recreate face normal vector
+ " vec3 r_norm = vec3(n.xy, dir * (1.0 - length(n.xy)));"
+ " vec3 light = normalize(vec3(-0.4,0.4,-1.0));"
+ " float l = (1.0 + dot(r_norm, light)) / 2.0;"
+ " l = 0.4 + l * 0.6;"
+ " l = (l + (0.9 + clamp(a_pos.z / 4096.0, 0.0, 0.2))) / 2.0;"
+ " color = vec4(l, l, l-0.02, 1.0);"
+ "}}}";
final static String extrusionFragmentShader = ""

View File

@ -21,10 +21,12 @@ import java.nio.ShortBuffer;
import org.oscim.backend.GL20;
import org.oscim.backend.GLAdapter;
import org.oscim.core.GeometryBuffer;
import org.oscim.core.GeometryBuffer.GeometryType;
import org.oscim.core.MapElement;
import org.oscim.core.Tile;
import org.oscim.renderer.BufferObject;
import org.oscim.renderer.MapRenderer;
import org.oscim.utils.FastMath;
import org.oscim.utils.Tessellator;
import org.oscim.utils.geom.LineClipper;
import org.oscim.utils.pool.Inlist;
@ -50,7 +52,7 @@ public class ExtrusionLayer extends RenderElement {
// indices for:
// 0. even sides, 1. odd sides, 2. roof, 3. roof outline
public int mIndiceCnt[] = { 0, 0, 0, 0 };
public int mIndiceCnt[] = { 0, 0, 0, 0, 0 };
public int mNumIndices = 0;
public int mNumVertices = 0;
@ -61,10 +63,15 @@ public class ExtrusionLayer extends RenderElement {
//private final static int IND_ODD_SIDE = 1;
private final static int IND_ROOF = 2;
private final static int IND_OUTLINE = 3;
private final static int IND_MESH = 4;
private static final int NORMAL_DIR_MASK = 0xFFFFFFFE;
public boolean compiled = false;
private final float mGroundResolution;
boolean filled;
public ExtrusionLayer(int level, float groundResolution, float[] colors) {
super(RenderElement.EXTRUSION);
this.level = level;
@ -73,14 +80,179 @@ public class ExtrusionLayer extends RenderElement {
mGroundResolution = groundResolution;
mVertices = mCurVertices = VertexItem.pool.get();
mIndices = new VertexItem[4];
mCurIndices = new VertexItem[4];
for (int i = 0; i < 4; i++)
mIndices = new VertexItem[5];
mCurIndices = new VertexItem[5];
for (int i = 0; i <= IND_MESH; i++)
mIndices[i] = mCurIndices[i] = VertexItem.pool.get();
mClipper = new LineClipper(0, 0, Tile.SIZE, Tile.SIZE);
}
public void add(MapElement element) {
if (element.type != GeometryType.TRIS)
return;
short[] index = element.index;
float[] points = element.points;
// roof indices for convex shapes
int i = mCurIndices[IND_MESH].used;
short[] indices = mCurIndices[IND_MESH].vertices;
int first = mNumVertices;
short[] vertices = mCurVertices.vertices;
int v = mCurVertices.used;
int vertexCnt = 0;
for (int k = 0, n = index.length; k < n;) {
if (index[k] < 0)
break;
// FIXME: workaround: dont overflow max index id.
if (mNumVertices + vertexCnt >= 1 << 16)
break;
if (i == VertexItem.SIZE) {
mCurIndices[IND_MESH].used = VertexItem.SIZE;
mCurIndices[IND_MESH].next = VertexItem.pool.get();
mCurIndices[IND_MESH] = mCurIndices[IND_MESH].next;
indices = mCurIndices[IND_MESH].vertices;
i = 0;
}
indices[i++] = (short) (first + vertexCnt);
indices[i++] = (short) (first + vertexCnt + 1);
indices[i++] = (short) (first + vertexCnt + 2);
int vtx1 = index[k++] * 3;
int vtx2 = index[k++] * 3;
int vtx3 = index[k++] * 3;
float vx1 = points[vtx1 + 0];
float vy1 = points[vtx1 + 1];
float vz1 = points[vtx1 + 2];
float vx2 = points[vtx2 + 0];
float vy2 = points[vtx2 + 1];
float vz2 = points[vtx2 + 2];
float vx3 = points[vtx3 + 0];
float vy3 = points[vtx3 + 1];
float vz3 = points[vtx3 + 2];
float ax = vx2 - vx1;
float ay = vy2 - vy1;
float az = vz2 - vz1;
float bx = vx3 - vx1;
float by = vy3 - vy1;
float bz = vz3 - vz1;
float cx = ay * bz - az * by;
float cy = az * bx - ax * bz;
float cz = ax * by - ay * bx;
double len = Math.sqrt(cx * cx + cy * cy + cz * cz);
// packing the normal in two bytes
int mx = FastMath.clamp(127 + (int) ((cx / len) * 128), 0, 0xff);
int my = FastMath.clamp(127 + (int) ((cy / len) * 128), 0, 0xff);
short normal = (short) ((my << 8) | (mx & NORMAL_DIR_MASK) | (cz > 0 ? 1 : 0));
if (v == VertexItem.SIZE) {
mCurVertices.used = VertexItem.SIZE;
mCurVertices.next = VertexItem.pool.get();
mCurVertices = mCurVertices.next;
vertices = mCurVertices.vertices;
v = 0;
}
double s = S * Tile.SIZE / 4096;
vertices[v++] = (short) (vx1 * s);
vertices[v++] = (short) (vy1 * s);
vertices[v++] = (short) (vz1 * s);
vertices[v++] = (short) normal;
vertices[v++] = (short) (vx2 * s);
vertices[v++] = (short) (vy2 * s);
vertices[v++] = (short) (vz2 * s);
vertices[v++] = (short) normal;
vertices[v++] = (short) (vx3 * s);
vertices[v++] = (short) (vy3 * s);
vertices[v++] = (short) (vz3 * s);
vertices[v++] = (short) normal;
vertexCnt += 3;
}
mCurIndices[IND_MESH].used = i;
mCurVertices.used = v;
mNumVertices += vertexCnt; //(vertexCnt / 3);
}
public void addNoNormal(MapElement element) {
if (element.type != GeometryType.TRIS)
return; //FIXME throw
short[] index = element.index;
float[] points = element.points;
//log.debug("add " + Arrays.toString(index));
//log.debug("add " + Arrays.toString(points));
// current vertex id
int startVertex = mNumVertices;
// roof indices for convex shapes
int i = mCurIndices[IND_MESH].used;
short[] indices = mCurIndices[IND_MESH].vertices;
int first = startVertex;
for (int k = 0, n = index.length; k < n;) {
if (index[k] < 0)
break;
if (i == VertexItem.SIZE) {
mCurIndices[IND_MESH].used = VertexItem.SIZE;
mCurIndices[IND_MESH].next = VertexItem.pool.get();
mCurIndices[IND_MESH] = mCurIndices[IND_MESH].next;
indices = mCurIndices[IND_MESH].vertices;
i = 0;
}
indices[i++] = (short) (first + index[k++]);
indices[i++] = (short) (first + index[k++]);
indices[i++] = (short) (first + index[k++]);
}
mCurIndices[IND_MESH].used = i;
short[] vertices = mCurVertices.vertices;
int v = mCurVertices.used;
int vertexCnt = element.pointPos;
for (int j = 0; j < vertexCnt;) {
/* add bottom and top vertex for each point */
if (v == VertexItem.SIZE) {
mCurVertices.used = VertexItem.SIZE;
mCurVertices.next = VertexItem.pool.get();
mCurVertices = mCurVertices.next;
vertices = mCurVertices.vertices;
v = 0;
}
// set coordinate
vertices[v++] = (short) (points[j++] * S);
vertices[v++] = (short) (points[j++] * S);
vertices[v++] = (short) (points[j++] * S);
v++;
}
mCurVertices.used = v;
mNumVertices += (vertexCnt / 3);
}
public void add(MapElement element, float height, float minHeight) {
short[] index = element.index;
@ -156,7 +328,7 @@ public class ExtrusionLayer extends RenderElement {
if (i == VertexItem.SIZE) {
mCurIndices[IND_ROOF].used = VertexItem.SIZE;
mCurIndices[IND_ROOF].next = VertexItem.pool.get();
mCurIndices[IND_ROOF] = mCurIndices[2].next;
mCurIndices[IND_ROOF] = mCurIndices[IND_ROOF].next;
indices = mCurIndices[IND_ROOF].vertices;
i = 0;
}
@ -367,7 +539,7 @@ public class ExtrusionLayer extends RenderElement {
return;
mNumIndices = 0;
for (int i = 0; i < 4; i++) {
for (int i = 0; i <= IND_MESH; i++) {
for (VertexItem vi = mIndices[i]; vi != null; vi = vi.next) {
sbuf.put(vi.vertices, 0, vi.used);
mIndiceCnt[i] += vi.used;
@ -375,6 +547,8 @@ public class ExtrusionLayer extends RenderElement {
mNumIndices += mIndiceCnt[i];
}
//log.debug("compile" + mNumIndices + " / " + mNumVertices);
int size = mNumIndices * 2;
vboIndices = BufferObject.get(GL20.GL_ELEMENT_ARRAY_BUFFER, size);
vboIndices.loadBufferData(sbuf.flip(), size);
@ -405,7 +579,7 @@ public class ExtrusionLayer extends RenderElement {
vboIndices = BufferObject.release(vboIndices);
vboVertices = BufferObject.release(vboVertices);
} else {
for (int i = 0; i < 4; i++)
for (int i = 0; i <= IND_MESH; i++)
mIndices[i] = VertexItem.pool.releaseAll(mIndices[i]);
mIndices = null;