FloatBuffer[] vertexBuffers = new FloatBuffer[bevelObject.size()];
FloatBuffer[] normalBuffers = new FloatBuffer[bevelObject.size()];
IndexBuffer[] indexBuffers = new IndexBuffer[bevelObject.size()];
for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) {
Mesh mesh = bevelObject.get(geomIndex).getMesh();
Vector3f[] positions = BufferUtils.getVector3Array(mesh.getFloatBuffer(Type.Position));
Vector3f[] bevelPoints = this.transformToFirstLineOfBevelPoints(positions, curvePoints[0], curvePoints[1]);
List<Vector3f[]> bevels = new ArrayList<Vector3f[]>(curvePoints.length);
bevels.add(bevelPoints);
vertexBuffers[geomIndex] = BufferUtils.createFloatBuffer(bevelPoints.length * 3 * curvePoints.length * (smooth ? 1 : 6));
for (int i = 1; i < curvePoints.length - 1; ++i) {
bevelPoints = this.transformBevel(bevelPoints, curvePoints[i - 1], curvePoints[i], curvePoints[i + 1]);
bevels.add(bevelPoints);
}
bevelPoints = this.transformBevel(bevelPoints, curvePoints[curvePoints.length - 2], curvePoints[curvePoints.length - 1], null);
bevels.add(bevelPoints);
if (bevels.size() > 2) {
// changing the first and last bevel so that they are parallel to their neighbours (blender works this way)
// notice this implicates that the distances of every corresponding point in th two bevels must be identical and
// equal to the distance between the points on curve that define the bevel position
// so instead doing complicated rotations on each point we will simply properly translate each of them
int[][] pointIndexes = new int[][] { { 0, 1 }, { curvePoints.length - 1, curvePoints.length - 2 } };
for (int[] indexes : pointIndexes) {
float distance = curvePoints[indexes[1]].subtract(curvePoints[indexes[0]], subtractResult).length();
Vector3f[] bevel = bevels.get(indexes[0]);
Vector3f[] nextBevel = bevels.get(indexes[1]);
for (int i = 0; i < bevel.length; ++i) {
float d = bevel[i].subtract(nextBevel[i], subtractResult).length();
subtractResult.normalizeLocal().multLocal(distance - d);
bevel[i].addLocal(subtractResult);
}
}
}
// apply scales to the bevels
float lengthAlongCurve = 0;
for (int i = 0; i < curvePoints.length; ++i) {
if (i > 0) {
lengthAlongCurve += curvePoints[i].subtract(curvePoints[i - 1], subtractResult).length();
}
float taperScale = this.getTaperScale(taperObject, i == 0 ? 0 : lengthAlongCurve / curveLength);
this.applyScale(bevels.get(i), curvePoints[i], taperScale);
}
if (smooth) {// add everything to the buffer
for (Vector3f[] bevel : bevels) {
for (Vector3f d : bevel) {
vertexBuffers[geomIndex].put(d.x);
vertexBuffers[geomIndex].put(d.y);
vertexBuffers[geomIndex].put(d.z);
}
}
} else {// add vertices to the buffer duplicating them so that every vertex belongs only to a single triangle
for (int i = 0; i < curvePoints.length - 1; ++i) {
for (int j = 0; j < bevelPoints.length - 1; ++j) {
// first triangle
vertexBuffers[geomIndex].put(bevels.get(i)[j].x);
vertexBuffers[geomIndex].put(bevels.get(i)[j].y);
vertexBuffers[geomIndex].put(bevels.get(i)[j].z);
vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].x);
vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].y);
vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].z);
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].x);
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].y);
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].z);
// second triangle
vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].x);
vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].y);
vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].z);
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j + 1].x);
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j + 1].y);
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j + 1].z);
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].x);
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].y);
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].z);
}
}
}
indexBuffers[geomIndex] = this.generateIndexes(bevelPoints.length, curvePoints.length, smooth);
normalBuffers[geomIndex] = this.generateNormals(indexBuffers[geomIndex], vertexBuffers[geomIndex], smooth);
}
// creating and returning the result
List<Geometry> result = new ArrayList<Geometry>(vertexBuffers.length);
Float oneReferenceToCurveLength = new Float(curveLength);// its important for array modifier to use one reference here
for (int i = 0; i < vertexBuffers.length; ++i) {
Mesh mesh = new Mesh();
mesh.setBuffer(Type.Position, 3, vertexBuffers[i]);
if (indexBuffers[i].getBuffer() instanceof IntBuffer) {
mesh.setBuffer(Type.Index, 3, (IntBuffer) indexBuffers[i].getBuffer());
} else {
mesh.setBuffer(Type.Index, 3, (ShortBuffer) indexBuffers[i].getBuffer());
}
mesh.setBuffer(Type.Normal, 3, normalBuffers[i]);
Geometry g = new Geometry("g" + i, mesh);
g.setUserData("curveLength", oneReferenceToCurveLength);
g.updateModelBound();
result.add(g);
}