Created
February 21, 2016 21:17
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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