Skip to content

Instantly share code, notes, and snippets.

@justasm
Created February 21, 2016 21:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justasm/50208beb948a72668ba3 to your computer and use it in GitHub Desktop.
Save justasm/50208beb948a72668ba3 to your computer and use it in GitHub Desktop.
Legacy VTK file loader. Decodes POLYDATA datasets from binary or ASCII VTK files to vertex, index and normal float buffers.
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* Decodes legacy VTK files to vertex, index and normal float buffers.
*/
public class VTKLoader {
private static final String LOG_TAG = VTKLoader.class.getSimpleName();
private static final int BYTES_PER_SHORT = 2;
private static final int BYTES_PER_FLOAT = 4;
// reference: http://www.vtk.org/VTK/img/file-formats.pdf
public static Buffer[] decode(InputStream inputStream) throws IOException {
// NOTE - DataInputStream is used as it eases reading mixed data from BINARY vtk files.
// Typically for text, you'd use InputStreamReader + BufferedReader.
DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(inputStream));
String line = dataInputStream.readLine();
if (!line.startsWith("# vtk DataFile Version ")) {
throw new IOException("File does not appear to be in a legacy VTK file format.");
}
Log.d(LOG_TAG, "Reading " + line);
// header
dataInputStream.readLine();
// file type
line = dataInputStream.readLine();
if (line.equals("ASCII")) {
return decodeASCII(line, dataInputStream);
} else if (line.equals("BINARY")) {
return decodeBinary(line, dataInputStream);
} else {
throw new IOException(LOG_TAG + " does not support " + line + " files.");
}
}
/**
* Decodes a legacy VTK BINARY file.
* Crosses fingers and hopes the endianness is correct as VTK.. does not enforce it.
*/
private static Buffer[] decodeBinary(String line, DataInputStream dataInputStream) throws IOException {
// dataset structure
while (!line.startsWith("DATASET ")) line = dataInputStream.readLine();
if (!line.split("DATASET ")[1].equals("POLYDATA")) {
throw new IOException(LOG_TAG + " only supports POLYDATA datasets.");
}
List<Buffer> decodedBuffers = new ArrayList<>(2);
// sections
while ((line = dataInputStream.readLine()) != null) {
String[] sectionData = line.split(" ");
if (sectionData[0].equals("POINTS")) {
Log.d(LOG_TAG, "Decoding " + sectionData[1] + " " + sectionData[0] + " of type " + sectionData[2]);
if (!"float".equals(sectionData[2]))
throw new IOException(LOG_TAG + " only supports floats.");
final int pointCount = Integer.valueOf(sectionData[1]);
final int floatCount = 3 * pointCount;
ByteBuffer bbVertices = ByteBuffer.allocateDirect(floatCount * BYTES_PER_FLOAT);
bbVertices.order(ByteOrder.nativeOrder());
FloatBuffer vertices = bbVertices.asFloatBuffer();
int i = 0;
byte[] floatBytes = new byte[BYTES_PER_FLOAT];
while (i < floatCount) {
dataInputStream.read(floatBytes);
vertices.put(ByteBuffer.wrap(floatBytes).order(ByteOrder.BIG_ENDIAN).getFloat());
i++;
}
// drop final newline
dataInputStream.read();
vertices.position(0);
decodedBuffers.add(vertices);
} else if (sectionData[0].equals("POLYGONS")) {
Log.d(LOG_TAG, "Decoding " + sectionData[1] + " " + sectionData[0] + ", total cell size " + sectionData[2]);
// don't count/store polygon point count /polygon
final int indexCount = Integer.valueOf(sectionData[2]) - Integer.valueOf(sectionData[1]);
ByteBuffer bbIndices = ByteBuffer.allocateDirect(indexCount * BYTES_PER_SHORT);
bbIndices.order(ByteOrder.nativeOrder());
ShortBuffer indices = bbIndices.asShortBuffer();
int i = 0;
byte[] intBytes = new byte[4];
while (i < indexCount) {
dataInputStream.read(intBytes);
int polyVertexCount = ByteBuffer.wrap(intBytes).order(ByteOrder.BIG_ENDIAN).getInt();
if (3 != polyVertexCount)
Log.e(LOG_TAG, "Non-triangle polygons of size " + polyVertexCount + " are not supported.");
for (int j = 0; j < polyVertexCount; j++) {
dataInputStream.read(intBytes);
// VTK provides indices as ints, but we store them as shorts
indices.put((short) ByteBuffer.wrap(intBytes).order(ByteOrder.BIG_ENDIAN).getInt());
i++;
}
}
// drop final newline
dataInputStream.read();
indices.position(0);
decodedBuffers.add(indices);
} else if (sectionData[0].equals("POINT_DATA")) {
final int pointCount = Integer.valueOf(sectionData[1]);
line = dataInputStream.readLine();
if (null == line) break; // some files have nothing after POINT_DATA..
sectionData = line.split(" ");
if (sectionData[0].equals("NORMALS")) {
Log.d(LOG_TAG, "Decoding " + pointCount + " " + sectionData[0] + " of type " + sectionData[2]);
if (!"float".equals(sectionData[2]))
throw new IOException(LOG_TAG + " only supports floats.");
final int floatCount = 3 * pointCount;
ByteBuffer bbNormals = ByteBuffer.allocateDirect(floatCount * BYTES_PER_FLOAT);
bbNormals.order(ByteOrder.nativeOrder());
FloatBuffer normals = bbNormals.asFloatBuffer();
int i = 0;
byte[] floatBytes = new byte[BYTES_PER_FLOAT];
while (i < floatCount) {
dataInputStream.read(floatBytes);
normals.put(ByteBuffer.wrap(floatBytes).order(ByteOrder.BIG_ENDIAN).getFloat());
i++;
}
// drop final newline
dataInputStream.read();
normals.position(0);
decodedBuffers.add(normals);
} else {
Log.e(LOG_TAG, "Discarding POINT_DATA " + sectionData[0]);
}
} else if (sectionData[0].equals("CELL_DATA")) {
// don't do much..
} else {
Log.e(LOG_TAG, "Dropping line: " + line);
}
}
Log.d(LOG_TAG, "Successfully decoded " + decodedBuffers.size() + " buffers.");
return decodedBuffers.toArray(new Buffer[decodedBuffers.size()]);
}
/**
* Decodes a legacy VTK ASCII file.
*/
private static Buffer[] decodeASCII(String line, DataInputStream dataInputStream) throws IOException {
// dataset structure
while (!line.startsWith("DATASET ")) line = dataInputStream.readLine();
if (!line.split("DATASET ")[1].equals("POLYDATA")) {
throw new IOException(LOG_TAG + " only supports POLYDATA datasets.");
}
List<Buffer> decodedBuffers = new ArrayList<>(2);
// sections
while ((line = dataInputStream.readLine()) != null) {
String[] sectionData = line.split(" ");
if (sectionData[0].equals("POINTS")) {
Log.d(LOG_TAG, "Decoding " + sectionData[1] + " " + sectionData[0] + " of type " + sectionData[2]);
if (!"float".equals(sectionData[2]))
throw new IOException(LOG_TAG + " only supports floats.");
final int pointCount = Integer.valueOf(sectionData[1]);
final int floatCount = 3 * pointCount;
ByteBuffer bbVertices = ByteBuffer.allocateDirect(floatCount * BYTES_PER_FLOAT);
bbVertices.order(ByteOrder.nativeOrder());
FloatBuffer vertices = bbVertices.asFloatBuffer();
int i = 0;
while (i < floatCount) {
line = dataInputStream.readLine();
String[] splitLine = line.split(" ");
for (int j = 0; j < splitLine.length; j++) {
vertices.put(Float.valueOf(splitLine[j]));
i++;
}
}
vertices.position(0);
decodedBuffers.add(vertices);
} else if (sectionData[0].equals("POLYGONS")) {
Log.d(LOG_TAG, "Decoding " + sectionData[1] + " " + sectionData[0] + ", total cell size " + sectionData[2]);
// don't count/store polygon point count /polygon
final int indexCount = Integer.valueOf(sectionData[2]) - Integer.valueOf(sectionData[1]);
ByteBuffer bbIndices = ByteBuffer.allocateDirect(indexCount * BYTES_PER_SHORT);
bbIndices.order(ByteOrder.nativeOrder());
ShortBuffer indices = bbIndices.asShortBuffer();
int i = 0;
while (i < indexCount) {
line = dataInputStream.readLine();
String[] splitLine = line.split(" ");
if (!"3".equals(splitLine[0]))
Log.e(LOG_TAG, "Non-triangle polygons of size " + splitLine[0] + " are not supported.");
for (int j = 1; j < splitLine.length; j++) {
indices.put(Short.valueOf(splitLine[j]));
i++;
}
}
indices.position(0);
decodedBuffers.add(indices);
} else if (sectionData[0].equals("TRIANGLE_STRIPS")) {
Log.d(LOG_TAG, "Decoding " + sectionData[1] + " " + sectionData[0] + ", total cell list size " + sectionData[2]);
final int degenerateTriangleIndexCount = (Integer.valueOf(sectionData[1]) - 1) * 2;
final int listedIndexCount = Integer.valueOf(sectionData[2]) - Integer.valueOf(sectionData[1]);
final int totalIndexCount = listedIndexCount + degenerateTriangleIndexCount;
ByteBuffer bbIndices = ByteBuffer.allocateDirect(totalIndexCount * BYTES_PER_SHORT);
bbIndices.order(ByteOrder.nativeOrder());
ShortBuffer indices = bbIndices.asShortBuffer();
int i = 0;
while (i < totalIndexCount) {
line = dataInputStream.readLine();
String[] splitLine = line.split(" ");
if (i != 0) {
// add degenerate index at start of new strip
indices.put(Short.valueOf(splitLine[1]));
i++;
}
for (int j = 1; j < splitLine.length; j++) {
indices.put(Short.valueOf(splitLine[j]));
i++;
}
if (i < totalIndexCount) {
// add degenerate index at end of new strip
indices.put(Short.valueOf(splitLine[splitLine.length - 1]));
i++;
}
}
indices.position(0);
decodedBuffers.add(indices);
} else if (sectionData[0].equals("POINT_DATA")) {
final int pointCount = Integer.valueOf(sectionData[1]);
line = dataInputStream.readLine();
if (null == line) break; // some files have nothing after POINT_DATA..
sectionData = line.split(" ");
if (sectionData[0].equals("NORMALS")) {
Log.d(LOG_TAG, "Decoding " + pointCount + " " + sectionData[0] + " of type " + sectionData[2]);
if (!"float".equals(sectionData[2]))
throw new IOException(LOG_TAG + " only supports floats.");
final int floatCount = 3 * pointCount;
ByteBuffer bbNormals = ByteBuffer.allocateDirect(floatCount * BYTES_PER_FLOAT);
bbNormals.order(ByteOrder.nativeOrder());
FloatBuffer normals = bbNormals.asFloatBuffer();
int i = 0;
while (i < floatCount) {
line = dataInputStream.readLine();
String[] splitLine = line.split(" ");
for (int j = 0; j < splitLine.length; j++) {
normals.put(Float.valueOf(splitLine[j]));
i++;
}
}
normals.position(0);
decodedBuffers.add(normals);
} else {
Log.e(LOG_TAG, "Discarding POINT_DATA " + sectionData[0]);
}
} else if (sectionData[0].equals("CELL_DATA")) {
// don't do much..
}
}
Log.d(LOG_TAG, "Successfully decoded " + decodedBuffers.size() + " buffers.");
return decodedBuffers.toArray(new Buffer[decodedBuffers.size()]);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment