/*
* Copyright 2013 Hannes Janetzek
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
* 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.utils;
import java.util.Arrays;
import org.oscim.renderer.bucket.VertexData;
import org.oscim.utils.math.MathUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Tessellator {
static final Logger log = LoggerFactory.getLogger(Tessellator.class);
/**
* Special version for ExtrusionLayer to match indices with vertex
* positions.
*/
public static int tessellate(float[] points, int ppos, int numPoints, int[] index,
int ipos, int numRings, int vertexOffset, VertexData outTris) {
int buckets = FastMath.log2(MathUtils.nextPowerOfTwo(numPoints));
buckets -= 2;
//log.debug("tess use {}", buckets);
TessJNI tess = new TessJNI(buckets);
tess.addContour2D(index, points, ipos, numRings);
//log.debug("tess ipos:{} rings:{}", ipos, numRings);
if (!tess.tesselate())
return 0;
int nverts = tess.getVertexCount() * 2;
int nelems = tess.getElementCount() * 3;
//log.debug("tess elems:{} verts:{} points:{}", nelems, nverts, numPoints);
if (numPoints != nverts) {
log.debug("tess ----- skip poly: " + nverts + " " + numPoints);
tess.dispose();
return 0;
}
int sumIndices = 0;
VertexData.Chunk vd = outTris.obtainChunk();
for (int offset = 0; offset < nelems;) {
int size = nelems - offset;
if (VertexData.SIZE == vd.used) {
vd = outTris.obtainChunk();
}
if (size > VertexData.SIZE - vd.used)
size = VertexData.SIZE - vd.used;
tess.getElementsWithInputVertexIds(vd.vertices, vd.used, offset, size);
int start = vd.used;
int end = start + size;
short[] indices = vd.vertices;
for (int i = start; i < end; i++) {
if (indices[i] < 0) {
log.debug(">>>> eeek {} {} {}",
start, end,
Arrays.toString(Arrays.copyOfRange(indices, start, end)));
break;
}
indices[i] *= 2;
}
/* when a ring has an odd number of points one (or rather two)
* additional vertices will be added. so the following rings
* needs extra offset */
int shift = 0;
for (int i = 0, m = numRings - 1; i < m; i++) {
shift += (index[ipos + i]);
/* even number of points? */
if (((index[ipos + i] >> 1) & 1) == 0)
continue;
for (int j = start; j < end; j++)
if (indices[j] >= shift)
indices[j] += 2;
shift += 2;
}
/* shift by vertexOffset */
for (int i = start; i < end; i++)
indices[i] += vertexOffset;
sumIndices += size;
vd.used += size;
outTris.releaseChunk();
offset += size;
}
tess.dispose();
return sumIndices;
}
// private static final int RESULT_VERTICES = 0;
// private static final int RESULT_TRIANGLES = 1;
//
// /**
// * Special version for ExtrusionLayer to match indices with vertex
// * positions.
// */
// public static int tessellate(float[] points, int ppos, int plen, int[] index,
// int ipos, int rings, int vertexOffset, VertexData outTris) {
//
// int[] result = new int[2];
//
// int numPoints = 0;
// for (int i = 0; i < rings; i++)
// numPoints += index[ipos + i];
//
// long ctx = Tessellator.tessellate(points, ppos, index, ipos, rings, result);
// if ((numPoints / 2) < result[RESULT_VERTICES]) {
// log.debug("skip poly: " + Arrays.toString(result) + " " + numPoints);
// Tessellator.tessFinish(ctx);
// return 0;
// }
//
// int cnt;
// int sumIndices = 0;
//
// VertexData.Chunk vd = outTris.obtainChunk();
//
// while ((cnt = Tessellator.tessGetIndicesWO(ctx, vd.vertices, vd.used)) > 0) {
// int start = vd.used;
// int end = start + cnt;
// short[] v = vd.vertices;
//
// for (int i = start; i < end; i++)
// v[i] *= 2;
//
// /* when a ring has an odd number of points one (or rather two)
// * additional vertices will be added. so the following rings
// * needs extra offset */
// int shift = 0;
// for (int i = 0, m = rings - 1; i < m; i++) {
// shift += (index[ipos + i]);
//
// /* even number of points? */
// if (((index[ipos + i] >> 1) & 1) == 0)
// continue;
//
// for (int j = start; j < end; j++)
// if (v[j] >= shift)
// v[j] += 2;
//
// shift += 2;
// }
//
// /* shift by vertexOffset */
// for (int i = start; i < end; i++)
// v[i] += vertexOffset;
//
// sumIndices += cnt;
//
// vd.used += cnt;
// outTris.releaseChunk();
//
// if (vd.used == VertexData.SIZE) {
// /* gets next item since this one is full */
// vd = outTris.obtainChunk();
// continue;
// }
// /* no more indices to get. */
// break;
// }
//
// Tessellator.tessFinish(ctx);
//
// return sumIndices;
// }
//
// /**
// * Untested!
// */
// public static int tessellate(GeometryBuffer geom, GeometryBuffer out) {
//
// int[] result = new int[2];
//
// int numRings = 0;
// int numPoints = 0;
//
// for (int i = 0; i < geom.indexPos; i++) {
// if (geom.index[i] > 0) {
// numRings++;
// numPoints += geom.index[i];
// } else
// break;
// }
//
// long ctx = Tessellator.tessellate(geom.points, 0,
// geom.index, 0,
// numRings, result);
//
// boolean verticesAdded = false;
// if (numPoints < result[RESULT_VERTICES] * 2) {
// //log.debug("grow vertices" + geom.pointPos);
// verticesAdded = true;
// }
//
// if (out == null) {
// /* overwrite geom contents */
// out = geom;
// if (verticesAdded) {
// out.ensurePointSize(result[RESULT_VERTICES], false);
// Tessellator.tessGetVerticesFloat(ctx, out.points);
// }
// } else {
// out.ensurePointSize(result[RESULT_VERTICES], false);
//
// if (verticesAdded) {
// Tessellator.tessGetVerticesFloat(ctx, out.points);
// } else {
// System.arraycopy(geom.points, 0, out.points, 0, numPoints);
// }
// }
//
// out.ensureIndexSize(result[RESULT_TRIANGLES * 3], false);
// Tessellator.tessGetIndices(ctx, out.index);
//
// Tessellator.tessFinish(ctx);
//
// return 1;
// }
//
// /* FIXME This modifies geom ?! */
// public static int tessellate(GeometryBuffer geom, float scale,
// VertexData outPoints, VertexData outTris, int vertexOffset) {
//
// int numIndices = 0;
// int indexPos = 0;
// int pointPos = 0;
// int indexEnd = geom.index.length;
//
// int[] result = new int[2];
//
// float s = scale;
// scale = 1;
//
// for (int idx = 0; idx < indexEnd && geom.index[idx] > 0; idx++) {
// indexPos = idx;
//
// int numRings = 1;
// int numPoints = geom.index[idx++];
//
// for (; idx < indexEnd && geom.index[idx] > 0; idx++) {
// numRings++;
// numPoints += geom.index[idx];
// }
//
// /* FIXME !!! */
// for (int i = pointPos; i < pointPos + numPoints; i += 2) {
// geom.points[i + 0] = (int) (geom.points[i + 0] * s);
// geom.points[i + 1] = (int) (geom.points[i + 1] * s);
// }
//
// long ctx = Tessellator.tessellate(geom.points, pointPos,
// geom.index, indexPos,
// numRings, result);
//
// if (result[RESULT_VERTICES] == 0 || result[RESULT_TRIANGLES] == 0) {
// log.debug("ppos " + pointPos + " ipos:" + indexPos +
// " rings:" + numRings + " " + Arrays.toString(geom.index));
// continue;
// }
//
// pointPos += numPoints;
//
// while (true) {
// VertexData.Chunk vd = outTris.obtainChunk();
//
// int cnt = Tessellator.tessGetIndicesWO(ctx, vd.vertices, vd.used);
// if (cnt <= 0)
// break;
//
// /* shift by vertexOffset */
// for (int pos = vd.used, end = pos + cnt; pos < end; pos++)
// vd.vertices[pos] += vertexOffset;
//
// numIndices += cnt;
//
// vd.used += cnt;
// outTris.releaseChunk();
//
// if (vd.used < VertexData.SIZE)
// break;
// }
//
// while (true) {
// VertexData.Chunk vd = outPoints.obtainChunk();
//
// int cnt = Tessellator.tessGetVerticesWO(ctx, vd.vertices, vd.used, scale);
// if (cnt <= 0)
// break;
//
// vertexOffset += cnt >> 1;
//
// vd.used += cnt;
// outPoints.releaseChunk();
//
// if (vd.used < VertexData.SIZE)
// break;
// }
//
// Tessellator.tessFinish(ctx);
//
// if (idx >= indexEnd || geom.index[idx] < 0)
// break;
// }
//
// if (vertexOffset > Short.MAX_VALUE) {
// log.debug("too much !!!" + Arrays.toString(geom.index));
// return 0;
// }
//
// return numIndices;
// }
//
// /**
// * @param points an array of x,y coordinates
// * @param pos position in points array
// * @param index geom indices
// * @param ipos position in index array
// * @param numRings number of rings in polygon == outer(1) + inner rings
// * @param result contains number of vertices and number of triangles
// * @return context - must be freed with tessFinish()
// */
// protected static native long tessellate(float[] points, int pos,
// int[] index, int ipos, int numRings, int[] result);
//
// protected static native void tessFinish(long ctx);
//
// protected static native int tessGetVertices(long ctx, short[] coordinates, float scale);
//
// protected static native int tessGetVerticesWO(long ctx, short[] coordinates,
// int offset, float scale);
//
// protected static native int tessGetVerticesFloat(long ctx, float[] coordinates);
//
// protected static native int tessGetIndices(long ctx, int[] indices);
//
// protected static native int tessGetIndicesWO(long ctx, short[] indices, int offset);
}