diff --git a/src/org/oscim/renderer/layer/ExtrusionLayer.java b/src/org/oscim/renderer/layer/ExtrusionLayer.java index bd77b47f..ee1cb623 100644 --- a/src/org/oscim/renderer/layer/ExtrusionLayer.java +++ b/src/org/oscim/renderer/layer/ExtrusionLayer.java @@ -19,7 +19,9 @@ import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; +import org.oscim.core.Tile; import org.oscim.renderer.GLRenderer; +import org.oscim.utils.LineClipper; import org.oscim.view.MapView; import org.quake.triangle.TriangleJNI; @@ -32,38 +34,54 @@ import android.util.Log; public class ExtrusionLayer extends Layer { private final static String TAG = ExtrusionLayer.class.getName(); private static final float S = GLRenderer.COORD_MULTIPLIER; - public int mIndicesBufferID; - public int mVertexBufferID; - public int mNumIndices = 0; private int mNumVertices = 0; private VertexPoolItem mVertices, mCurVertices; private VertexPoolItem mIndices[], mCurIndices[]; + private LineClipper mClipper; public int mIndiceCnt[] = { 0, 0, 0 }; + public int mIndicesBufferID; + public int mVertexBufferID; + public int mNumIndices = 0; + + private final static int IND_EVEN_SIDE = 0; + private final static int IND_ODD_SIDE = 1; + private final static int IND_ROOF = 2; + private final static int IND_OUTLINE = 3; public ExtrusionLayer(int level) { this.type = Layer.EXTRUSION; this.layer = level; mVertices = mCurVertices = VertexPool.get(); - mIndices = new VertexPoolItem[3]; - mCurIndices = new VertexPoolItem[3]; - mIndices[0] = mCurIndices[0] = VertexPool.get(); - mIndices[1] = mCurIndices[1] = VertexPool.get(); - mIndices[2] = mCurIndices[2] = VertexPool.get(); + // indices for + // 0. even sides + // 1. odd sides + // 2. roof + // 3. roof outline + mIndices = new VertexPoolItem[4]; + mCurIndices = new VertexPoolItem[4]; + for (int i = 0; i < 4; i++) + mIndices[i] = mCurIndices[i] = VertexPool.get(); + + mClipper = new LineClipper(0, 0, Tile.TILE_SIZE, Tile.TILE_SIZE); } public void addBuildings(float[] points, short[] index, int height) { - int complex = 0; + + // start outer ring + int outer = 0; + boolean simple = true; + int startVertex = mNumVertices; if (height == 0) height = 400; else height *= 40; - for (int i = 0, pos = 0, n = index.length; i < n; i++) { - int length = index[i]; + for (int ipos = 0, ppos = 0, n = index.length; ipos < n; ipos++) { + int length = index[ipos]; // end marker if (length < 0) @@ -71,29 +89,89 @@ public class ExtrusionLayer extends Layer { // start next polygon if (length == 0) { - complex = i + 1; + outer = ipos + 1; + startVertex = mNumVertices; simple = true; continue; } + // we dont need to add duplicate end/start point + int len = length; + if (!MapView.enableClosePolygons) + len -= 2; + // need at least three points - if (length < 6) { - pos += length; + if (len < 6) { + ppos += length; continue; } // check if polygon contains inner rings - //if (simple && ((i < n - 1) && (index[i + 1] > 0))) - // simple = false; + if (simple && (ipos < n - 1) && (index[ipos + 1] > 0)) + simple = false; - addOutline(points, pos, length, height, simple); - pos += length; + boolean convex = addOutline(points, ppos, len, height, simple); + if (simple && (convex || len <= 8)) + addRoofSimple(startVertex, len); + else if (ipos == outer) // only add roof once + //addRoof(startVertex, pos, len, points); + addRoof(startVertex, index, ipos, points, ppos); + + ppos += length; } } - private void addOutline(float[] points, int pos, int len, float height, boolean simple) { - if (!MapView.enableClosePolygons) - len -= 2; + private void addRoofSimple(int startVertex, int len) { + + // roof indices for convex shapes + int i = mCurIndices[IND_ROOF].used; + short[] indices = mCurIndices[IND_ROOF].vertices; + short first = (short) (startVertex + 1); + + for (int k = 0; k < len - 4; k += 2) { + if (i == VertexPoolItem.SIZE) { + mCurIndices[IND_ROOF].used = VertexPoolItem.SIZE; + mCurIndices[IND_ROOF].next = VertexPool.get(); + mCurIndices[IND_ROOF] = mCurIndices[2].next; + indices = mCurIndices[IND_ROOF].vertices; + i = 0; + } + indices[i++] = first; + indices[i++] = (short) (first + k + 4); + indices[i++] = (short) (first + k + 2); + } + mCurIndices[IND_ROOF].used = i; + } + + private void addRoof(int startVertex, short[] index, int ipos, float[] points, int ppos) { + int len = 0; + int rings = 0; + + // get sum of points in polygon + for (int i = ipos, n = index.length; i < n && index[i] > 0; i++) { + len += index[i]; + rings++; + } + + // triangulate up to 200 points (limited only by prepared buffers) + if (len > 400) { + Log.d(TAG, ">>> skip building : " + len + " <<<"); + return; + } + + int used = triangulate(points, ppos, len, index, ipos, rings, + startVertex + 1, mCurIndices[IND_ROOF]); + + if (used > 0) { + // get back to the last item added.. + VertexPoolItem it = mIndices[IND_ROOF]; + while (it.next != null) + it = it.next; + mCurIndices[IND_ROOF] = it; + } + } + + private boolean addOutline(float[] points, int pos, int len, float height, boolean convex) { // add two vertices for last face to make zigzag indices work boolean addFace = (len % 4 != 0); @@ -101,7 +179,6 @@ public class ExtrusionLayer extends Layer { // Log.d(TAG, "add: " + addFace + " " + len + " (" + pos + ")"); int vertexCnt = len + (addFace ? 2 : 0); - int indicesCnt = (len >> 1) * 6; short h = (short) height; @@ -113,7 +190,6 @@ public class ExtrusionLayer extends Layer { float vx = nx - cx; float vy = ny - cy; float ca = (float) Math.sqrt(vx * vx + vy * vy); - float pa = ca; float ux = vx; float uy = vy; @@ -125,12 +201,14 @@ public class ExtrusionLayer extends Layer { boolean even = true; + int changeX = 0; + int changeY = 0; + short[] vertices = mCurVertices.vertices; int v = mCurVertices.used; - int convex = 0; - - for (int i = 0; i < len; i += 2, v += 8) { + for (int i = 2; i < len + 2; i += 2, v += 8) { + /* add bottom and top vertex for each point */ cx = nx; cy = ny; @@ -142,46 +220,53 @@ public class ExtrusionLayer extends Layer { v = 0; } + // set coordinate vertices[v + 0] = vertices[v + 4] = (short) (cx * S); vertices[v + 1] = vertices[v + 5] = (short) (cy * S); + // set height vertices[v + 2] = 0; vertices[v + 6] = h; - if (i < len - 2) { - nx = points[pos + i + 2]; - ny = points[pos + i + 3]; - - vx = nx - cx; - vy = ny - cy; - ca = (float) Math.sqrt(vx * vx + vy * vy); - - if (convex > -1) { - // TODO fix for straight line... - double dir = (vx * ux + vy * uy) / (ca * pa); - - if (convex == 0) - convex = dir > 0 ? 1 : 2; - else if (convex == 1) - convex = dir > 0 ? 1 : -1; - else - convex = dir > 0 ? -1 : 2; - } - vlight = vx > 0 ? (vx / ca) : -(vx / ca) - 0.1f; - color2 = (short) (200 + (50 * vlight)); + // calculate direction to next point + if (i < len) { + nx = points[pos + i + 0]; + ny = points[pos + i + 1]; } else { - color2 = fcolor; + nx = points[pos + 0]; + ny = points[pos + 1]; + //color2 = fcolor; } + vx = nx - cx; + vy = ny - cy; + ca = (float) Math.sqrt(vx * vx + vy * vy); + + vlight = vx > 0 ? (vx / ca) : -(vx / ca) - 0.1f; + color2 = (short) (200 + (50 * vlight)); + short c; if (even) c = (short) (color1 | color2 << 8); else c = (short) (color2 | color1 << 8); + // set lighting (direction) vertices[v + 3] = vertices[v + 7] = c; - pa = ca; + if (convex) { + // TODO simple polys with only one concave arc + // could be handled without special triangulation + + if ((ux < 0 ? 1 : -1) != (vx < 0 ? 1 : -1)) + changeX++; + if ((uy < 0 ? 1 : -1) != (vy < 0 ? 1 : -1)) + changeY++; + + if (changeX > 2 || changeY > 2) + convex = false; + } + ux = vx; uy = vy; color1 = color2; @@ -214,7 +299,7 @@ public class ExtrusionLayer extends Layer { mCurVertices.used = v; - // fill ZigZagQuadIndices(tm) + // fill ZigZagQuadIndices(tm) and outline indices for (int j = 0; j < 2; j++) { short[] indices = mCurIndices[j].vertices; @@ -223,8 +308,32 @@ public class ExtrusionLayer extends Layer { // vertex id v = mNumVertices + (j * 2); + int ppos = pos + (j * 2); + + for (int k = j * 2; k < len; k += 4, ppos += 4) { + boolean accept; + if (k + 2 < len) { + accept = mClipper.clip( + (int) points[ppos], + (int) points[ppos + 1], + (int) points[ppos + 2], + (int) points[ppos + 3]); + } else { + accept = mClipper.clip( + (int) points[ppos], + (int) points[ppos + 1], + (int) points[pos + 0], + (int) points[pos + 1]); + } + + if (!accept) { + // Log.d(TAG, "omit line: " + // + points[ppos] + ":" + points[ppos + 1] + " " + // + points[ppos + 2] + ":" + points[ppos + 3]); + v += 4; + continue; + } - for (int k = j * 2; k < len; k += 4) { short s0 = (short) (v++); short s1 = (short) (v++); short s2 = (short) (v++); @@ -246,6 +355,7 @@ public class ExtrusionLayer extends Layer { s3 -= len; } } + indices[i++] = s0; indices[i++] = s1; indices[i++] = s2; @@ -256,76 +366,16 @@ public class ExtrusionLayer extends Layer { //System.out.println(" i:" + (mNumIndices + (k * 6)) // + "\t(" + s0 + "," + s1 + "," + s2 // + ")\t(" + s1 + "," + s3 + "," + s2 + ")"); + + // outline[cOut++] = s1; + // outline[cOut++] = s3; + } mCurIndices[j].used = i; } - if (simple && (len <= 8 || convex > 0)) { - //Log.d(TAG, len + " is simple " + convex); - - // roof indices for convex shapes - int i = mCurIndices[2].used; - short[] indices = mCurIndices[2].vertices; - short first = (short) (mNumVertices + 1); - - for (int k = 0; k < len - 4; k += 2) { - if (i == VertexPoolItem.SIZE) { - mCurIndices[2].used = VertexPoolItem.SIZE; - mCurIndices[2].next = VertexPool.get(); - mCurIndices[2] = mCurIndices[2].next; - indices = mCurIndices[2].vertices; - i = 0; - } - indices[i++] = first; - //if (convex != 2) { - // cw ? - indices[i++] = (short) (first + k + 4); - indices[i++] = (short) (first + k + 2); - // } else { - // indices[i++] = (short) (first + k + 2); - // indices[i++] = (short) (first + k + 4); - // } - - // System.out.println("indice:" + k + "\t" + indices[cnt - 3] + "," - // + indices[cnt - 2]+ "," + indices[cnt - 1]); - - indicesCnt += 3; - } - mCurIndices[2].used = i; - } else if (len < 400) { - // triangulate up to 200 points - short first = (short) (mNumVertices + 1); - int used = triangulate(points, pos, len, mCurIndices[2], first); - if (used > 0) { - indicesCnt += used; - // find the last item added.. - VertexPoolItem it = mIndices[2]; - while (it.next != null) - it = it.next; - mCurIndices[2] = it; - } - - // mCurIndices[2].next = VertexPool.get(); - // mCurIndices[2] = mCurIndices[2].next; - // short[] indices = mCurIndices[2].vertices; - // int used = triangulate(points, pos, len, indices); - // if (used > 0) { - // short first = (short) (mNumVertices + 1); - // for (int i = 0; i < used; i += 3) { - // indices[i] = (short) (indices[i] * 2 + first); - // short tmp = indices[i + 1]; - // indices[i + 1] = (short) (indices[i + 2] * 2 + first); - // indices[i + 2] = (short) (tmp * 2 + first); - // } - // mCurIndices[2].used = used; - // indicesCnt += used; - // } - } else - Log.d(TAG, "skip >>>>>>>>>> : " + len + " <<<<<<<<<<<<<"); - - //Log.d(TAG, "add building: " + vertexCnt); mNumVertices += vertexCnt; - mNumIndices += indicesCnt; + return convex; } public void compile(ShortBuffer sbuf) { @@ -406,10 +456,9 @@ public class ExtrusionLayer extends Layer { private static ShortBuffer sBuf; private static FloatBuffer fBuf; - public static synchronized int triangulate(float[] points, int pos, int len, - VertexPoolItem item, int first) { - - int numRings = 1; + public static synchronized int triangulate(float[] points, int ppos, int plen, short[] index, + int ipos, int rings, + int vertexOffset, VertexPoolItem item) { if (!initialized) { // FIXME also cleanup on shutdown! @@ -423,27 +472,27 @@ public class ExtrusionLayer extends Layer { } fBuf.clear(); - fBuf.put(points, pos, len); + fBuf.put(points, ppos, plen); sBuf.clear(); - sBuf.put((short) (len >> 1)); // all points - sBuf.put((short) (len >> 1)); // outer ring - //sBuf.put((short)4); // inner ring + sBuf.put((short) plen); // all points + sBuf.put(index, ipos, rings); - int numTris = TriangleJNI.triangulate(fBuf, numRings, sBuf, first); + int numTris = TriangleJNI.triangulate(fBuf, rings, sBuf, vertexOffset); int numIndices = numTris * 3; sBuf.limit(numIndices); sBuf.position(0); for (int k = 0, cnt = 0; k < numIndices; k += cnt) { - cnt = VertexPoolItem.SIZE - item.used; if (item.used == VertexPoolItem.SIZE) { item.next = VertexPool.get(); item = item.next; } + cnt = VertexPoolItem.SIZE - item.used; + if (k + cnt > numIndices) cnt = numIndices - k; @@ -451,25 +500,6 @@ public class ExtrusionLayer extends Layer { item.used += cnt; } - // sBuf.get(sIndices, 0, numIndices); - // - // short[] indices = item.vertices; - // int i = item.used; - // - // for (int k = 0; k < numIndices; k += 3) { - // if (i == VertexPoolItem.SIZE) { - // item.used = VertexPoolItem.SIZE; - // item.next = VertexPool.get(); - // item = item.next; - // indices = item.vertices; - // i = 0; - // } - // indices[i++] = sIndices[k + 0]; - // indices[i++] = sIndices[k + 1]; - // indices[i++] = sIndices[k + 2]; - // } - // item.used = i; - return numIndices; } } diff --git a/src/org/oscim/renderer/overlays/BuildingOverlay2.java b/src/org/oscim/renderer/overlays/BuildingOverlay2.java index 80b46add..9c2e0b89 100644 --- a/src/org/oscim/renderer/overlays/BuildingOverlay2.java +++ b/src/org/oscim/renderer/overlays/BuildingOverlay2.java @@ -111,7 +111,7 @@ public class BuildingOverlay2 extends RenderOverlay { // 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.81f, 0.80f, 0.79f, 0.7f }; + float mRoofColor[] = { 0.895f, 0.89f, 0.88f, 0.9f }; boolean debug = false; @Override @@ -156,7 +156,7 @@ public class BuildingOverlay2 extends RenderOverlay { return; } - int drawCount = 20; + int drawCount = 0; // draw to depth buffer MapTile[] tiles = mTileSet.tiles; for (int i = 0; i < mTileSet.cnt; i++) { @@ -169,27 +169,27 @@ public class BuildingOverlay2 extends RenderOverlay { continue; if (first) { + GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT); + GLES20.glUseProgram(buildingProgram); GLRenderer.enableVertexArrays(hBuildingVertexPosition, -1); - GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT); - GLES20.glEnable(GLES20.GL_DEPTH_TEST); GLES20.glEnable(GLES20.GL_CULL_FACE); GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL); - //GLES20.glCullFace(GLES20.GL_CW); - GLES20.glDepthMask(true); + GLES20.glEnable(GLES20.GL_DEPTH_TEST); GLES20.glDepthFunc(GLES20.GL_LESS); - GLES20.glUniform1i(hBuildingMode, 0); + GLES20.glDepthMask(true); GLES20.glColorMask(false, false, false, false); + GLES20.glUniform1i(hBuildingMode, 0); first = false; } - GLES20.glPolygonOffset(0, drawCount--); + 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 == 0) - drawCount = 20; + if (drawCount == 100) + drawCount = 0; setMatrix(pos, mv, proj, tiles[i], 1); GLES20.glUniformMatrix4fv(hBuildingMatrix, 1, false, mv, 0); @@ -209,10 +209,13 @@ public class BuildingOverlay2 extends RenderOverlay { // enable color buffer, use depth mask GLRenderer.enableVertexArrays(hBuildingVertexPosition, hBuildingLightPosition); GLES20.glColorMask(true, true, true, true); - GLES20.glDepthMask(false); + //GLES20.glDepthMask(false); GLES20.glDepthFunc(GLES20.GL_EQUAL); + //GLES20.glDepthFunc(GLES20.GL_EQUAL); - drawCount = 20; + drawCount = 0; + //GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL); + //GLES20.glPolygonOffset(0, -2); for (int i = 0; i < mTileSet.cnt; i++) { if (!tiles[i].isVisible || tiles[i].layers == null @@ -223,9 +226,9 @@ public class BuildingOverlay2 extends RenderOverlay { if (!el.compiled) continue; - GLES20.glPolygonOffset(0, drawCount--); - if (drawCount == 0) - drawCount = 20; + GLES20.glPolygonOffset(0, drawCount += 10); + if (drawCount == 100) + drawCount = 0; setMatrix(pos, mv, proj, tiles[i], 1); GLES20.glUniformMatrix4fv(hBuildingMatrix, 1, false, mv, 0); @@ -233,6 +236,7 @@ public class BuildingOverlay2 extends RenderOverlay { 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); @@ -262,7 +266,18 @@ public class BuildingOverlay2 extends RenderOverlay { GLES20.glDrawElements(GLES20.GL_TRIANGLES, el.mIndiceCnt[1], GLES20.GL_UNSIGNED_SHORT, el.mIndiceCnt[0] * 2); - GlUtils.checkGlError("..."); + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); + GlUtils.checkGlError(".1."); + + // GLRenderer.enableVertexArrays(hBuildingVertexPosition, -1); + // GLES20.glVertexAttribPointer(hBuildingVertexPosition, 3, + // GLES20.GL_SHORT, false, 16, 8); + // + // GLES20.glUniform1i(hBuildingMode, 0); + // GLES20.glUniform4f(hBuildingColor, 1.0f, 0.5f, 0.5f, 0.9f); + // GLES20.glDrawArrays(GLES20.GL_LINE_STRIP, 0, 10); + // + // GlUtils.checkGlError(".2."); } if (!first) { diff --git a/src/org/oscim/utils/GeometryUtils.java b/src/org/oscim/utils/GeometryUtils.java index fd1b5e15..3e52e719 100644 --- a/src/org/oscim/utils/GeometryUtils.java +++ b/src/org/oscim/utils/GeometryUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010, 2011, 2012 mapsforge.org + * Copyright 2012, 2013 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 @@ -17,8 +17,7 @@ package org.oscim.utils; import android.graphics.Point; /** - * - * + * @author Hannes Janetzek */ public final class GeometryUtils { /** diff --git a/src/org/oscim/utils/LineClipper.java b/src/org/oscim/utils/LineClipper.java index df5f8672..d795b256 100644 --- a/src/org/oscim/utils/LineClipper.java +++ b/src/org/oscim/utils/LineClipper.java @@ -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 * terms of the GNU Lesser General Public License as published by the Free Software @@ -16,31 +16,37 @@ package org.oscim.utils; import android.graphics.Point; -// from http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm +/** + * @author Hannes Janetzek + * taken from http://en.wikipedia.org/wiki/Cohen%E2%80%93 + * Sutherland_algorithm + */ public class LineClipper { - private static final int INSIDE = 0; // 0000 - private static int LEFT = 1; // 0001 - private static int RIGHT = 2; // 0010 - private static int BOTTOM = 4; // 0100 - private static int TOP = 8; // 1000 + public static final int INSIDE = 0; // 0000 + public static final int LEFT = 1; // 0001 + public static final int RIGHT = 2; // 0010 + public static final int BOTTOM = 4; // 0100 + public static final int TOP = 8; // 1000 // Compute the bit code for a point (x, y) using the clip rectangle // bounded diagonally by (xmin, ymin), and (xmax, ymax) private int xmin, xmax, ymin, ymax; - public boolean clip(Point p1, Point p2, int minx, int miny, int maxx, int maxy) { + public LineClipper(int minx, int miny, int maxx, int maxy) { this.xmin = minx; this.ymin = miny; this.xmax = maxx; this.ymax = maxy; - - return cohenSutherlandLineClip(p1.x, p1.y, p2.x, p2.y); } - private int outCode(int x, int y) { + public boolean clip(Point p1, Point p2) { + return clip(p1.x, p1.y, p2.x, p2.y); + } + + public int outCode(int x, int y) { int code; code = INSIDE; // initialised as being inside of clip window @@ -60,7 +66,7 @@ public class LineClipper { // CohenSutherland clipping algorithm clips a line from // P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with // diagonal from (xmin, ymin) to (xmax, ymax). - private boolean cohenSutherlandLineClip(int x0, int y0, int x1, int y1) { + public boolean clip(int x0, int y0, int x1, int y1) { // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle int outcode0 = outCode(x0, y0); int outcode1 = outCode(x1, y1);