LoaderSTL.java
/*
* Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.irurueta.geometry.io;
import java.io.File;
import java.io.IOException;
public class LoaderSTL extends Loader {
/**
* Constant defining the default value of maximum number of vertices to keep
* in a chunk. This is 65535, which corresponds to the maximum value allowed
* by graphical layer such as OpenGL when working with Vertex Buffer Objects.
*/
public static final int DEFAULT_MAX_VERTICES_IN_CHUNK = 0xffff;
/**
* Minimum allowed value for maximum number of vertices in chunk, which is
* one.
*/
public static final int MIN_MAX_VERTICES_IN_CHUNK = 1;
/**
* Amount of progress variation (1%) used to notify progress.
*/
public static final float PROGRESS_DELTA = 0.01f;
private LoaderIteratorSTL loaderIterator;
private int maxVerticesInChunk;
/**
* Constructor.
*/
public LoaderSTL() {
loaderIterator = null;
maxVerticesInChunk = DEFAULT_MAX_VERTICES_IN_CHUNK;
}
/**
* Constructor.
*
* @param maxVerticesInChunk Maximum number of vertices allowed in a chunk.
* Once this value is exceeded when loading a file, a new chunk of data is
* created.
* @throws IllegalArgumentException if maximum number of vertices allowed in
* a chunk is lower than 1.
*/
public LoaderSTL(final int maxVerticesInChunk) {
loaderIterator = null;
internalSetMaxVerticesInChunk(maxVerticesInChunk);
}
/**
* Constructor.
*
* @param f file to be loaded.
* @throws IOException if an I/O error occurs.
*/
public LoaderSTL(final File f) throws IOException {
super(f);
loaderIterator = null;
maxVerticesInChunk = DEFAULT_MAX_VERTICES_IN_CHUNK;
}
/**
* Constructor.
*
* @param f file to be loaded.
* @param maxVerticesInChunk Maximum number of vertices allowed in a chunk.
* Once this value is exceeded when loading a file, a new chunk of data is
* created.
* @throws IllegalArgumentException if maximum number of vertices allowed in
* a chunk is lower than 1.
* @throws IOException if an I/O error occurs.
*/
public LoaderSTL(final File f, final int maxVerticesInChunk) throws IOException {
super(f);
loaderIterator = null;
internalSetMaxVerticesInChunk(maxVerticesInChunk);
}
/**
* Constructor.
*
* @param listener listener to be notified of loading progress and when
* loading process starts or finishes.
*/
public LoaderSTL(final LoaderListener listener) {
super(listener);
maxVerticesInChunk = DEFAULT_MAX_VERTICES_IN_CHUNK;
}
/**
* Constructor.
*
* @param listener listener to be notified of loading progress and when
* loading process starts or finishes.
* @param maxVerticesInChunk Maximum number of vertices allowed in a chunk.
* Once this value is exceeded when loading a file, a new chunk of data is
* created.
* @throws IllegalArgumentException if maximum number of vertices allowed in
* a chunk is lower than 1.
*/
public LoaderSTL(final LoaderListener listener, final int maxVerticesInChunk) {
super(listener);
loaderIterator = null;
internalSetMaxVerticesInChunk(maxVerticesInChunk);
}
/**
* Constructor.
*
* @param f file to be loaded.
* @param listener listener to be notified of loading progress and when
* loading process starts or finishes.
* @throws IOException if an I/O error occurs.
*/
public LoaderSTL(final File f, final LoaderListener listener) throws IOException {
super(f, listener);
loaderIterator = null;
maxVerticesInChunk = DEFAULT_MAX_VERTICES_IN_CHUNK;
}
/**
* Constructor.
*
* @param f file to be loaded.
* @param listener listener to be notified of loading progress and when
* loading process starts or finishes.
* @param maxVerticesInChunk Maximum number of vertices allowed in a chunk.
* Once this value is exceeded when loading a file, a new chunk of data is
* created.
* @throws IllegalArgumentException if maximum number of vertices allowed in
* a chunk is lower than 1.
* @throws IOException if an I/O error occurs.
*/
public LoaderSTL(final File f, final LoaderListener listener, final int maxVerticesInChunk) throws IOException {
super(f, listener);
loaderIterator = null;
internalSetMaxVerticesInChunk(maxVerticesInChunk);
}
/**
* Sets maximum number of vertices allowed in a chunk.
* Once this value is exceeded when loading a file, a new chunk of data is
* created.
*
* @param maxVerticesInChunk maximum allowed number of vertices to be set.
* @throws IllegalArgumentException if provided value is lower than 1.
* @throws LockedException if this loader is currently loading a file.
*/
public void setMaxVerticesInChunk(final int maxVerticesInChunk) throws LockedException {
if (isLocked()) {
throw new LockedException();
}
internalSetMaxVerticesInChunk(maxVerticesInChunk);
}
/**
* Returns maximum number of vertices allowed in a chunk.
* Once this value is exceeded when loading a file, a new chunk of data is
* created.
*
* @return maximum number of vertices allowed in a chunk.
*/
public int getMaxVerticesInChunk() {
return maxVerticesInChunk;
}
/**
* If loader is ready to start loading a file.
* This is true once a file has been provided.
*
* @return true if ready to start loading a file, false otherwise.
*/
@Override
public boolean isReady() {
return hasFile();
}
/**
* Returns mesh format supported by this class, which is MESH_FORMAT_STL.
*
* @return mesh format supported by this class.
*/
@Override
public MeshFormat getMeshFormat() {
return MeshFormat.MESH_FORMAT_STL;
}
/**
* Determines if provided file is a valid file that can be read by this
* loader.
*
* @return true if file is valid, false otherwise.
* @throws LockedException raised if this instance is already locked.
* @throws IOException if an I/O error occurs.
*/
@Override
public boolean isValidFile() throws LockedException, IOException {
if (!hasFile()) {
throw new IOException();
}
if (isLocked()) {
throw new LockedException();
}
return true;
}
/**
* Starts the loading process of provided file.
* This method returns a LoaderIterator to start the iterative process to
* load a file in small chunks of data.
*
* @return a loader iterator to read the file in a step-by-step process.
* @throws LockedException raised if this instance is already locked.
* @throws NotReadyException raised if this instance is not yet ready.
* @throws IOException if an I/O error occurs.
* @throws LoaderException if file is corrupted or cannot be interpreted.
*/
@Override
public LoaderIterator load() throws LockedException, NotReadyException, IOException, LoaderException {
if (isLocked()) {
throw new LockedException();
}
if (!isReady()) {
throw new NotReadyException();
}
setLocked(true);
if (listener != null) {
listener.onLoadStart(this);
}
loaderIterator = new LoaderIteratorSTL(this);
loaderIterator.setListener(new LoaderIteratorListenerImpl(this));
return loaderIterator;
}
/**
* Returns name for the 3D object.
*
* @return name for the 3D object.
*/
protected String getSolidName() {
return loaderIterator != null ? loaderIterator.getSolidName() : null;
}
/**
* Gets number of vertices contained in the file.
*
* @return number of vertices contained in the file.
*/
protected Long getNumberOfVertices() {
return loaderIterator != null ? loaderIterator.getNumberOfVertices() : null;
}
/**
* Internal method to set maximum number of vertices allowed in a chunk.
* This method is reused both in the constructor and in the setter of
* maximum number of vertices allowed in a chunk.
*
* @param maxVerticesInChunk maximum allowed number of vertices to be set.
* @throws IllegalArgumentException if provided value is lower than 1.
*/
private void internalSetMaxVerticesInChunk(final int maxVerticesInChunk) {
if (maxVerticesInChunk < MIN_MAX_VERTICES_IN_CHUNK) {
throw new IllegalArgumentException();
}
this.maxVerticesInChunk = maxVerticesInChunk;
}
/**
* Internal listener to be notified when loading process finishes.
* This listener is used to free resources when loading process finishes.
*/
private class LoaderIteratorListenerImpl implements LoaderIteratorListener {
/**
* Reference to Loader loading an STL file.
*/
private final LoaderSTL loader;
public LoaderIteratorListenerImpl(final LoaderSTL loader) {
this.loader = loader;
}
/**
* Method to be notified when the loading process finishes.
*
* @param iterator iterator loading the file in chunks.
*/
@Override
public void onIteratorFinished(final LoaderIterator iterator) {
// because iterator is finished, we should allow subsequent calls to
// load method
try {
// attempt restart stream to initial position
reader.seek(0);
} catch (final Exception ignore) {
// this is the best effort operation, if it fails it is ignored
}
// on subsequent calls
if (listener != null) {
listener.onLoadEnd(loader);
}
setLocked(false);
}
}
/**
* Loader iterator in charge of loading file data in small chunks.
* Usually data is divided in chunks small enough that can be directly
* loaded by graphical layers such as OpenGL (which has a limit of 65535
* indices when using Vertex Buffer Objects, which increase graphical
* performance).
*/
private class LoaderIteratorSTL implements LoaderIterator {
/**
* Reference to loader loading STL file.
*/
private final LoaderSTL loader;
/**
* X coordinate of the latest point that has been read.
*/
private float coordX;
/**
* Y coordinate of the latest point that has been read.
*/
private float coordY;
/**
* Z coordinate of the latest point that has been read.
*/
private float coordZ;
/**
* X coordinate of the latest point normal that has been read.
*/
private float nX;
/**
* Y coordinate of the latest point normal that has been read.
*/
private float nY;
/**
* Z coordinate of the latest point normal that has been read.
*/
private float nZ;
// coordinates for bounding box in a chunk
/**
* X coordinate of the minimum point forming the bounding box in a chunk
* of data. This value will be updated while the chunk is being filled.
*/
private float minX;
/**
* Y coordinate of the minimum point forming the bounding box in a chunk
* of data. This value will be updated while the chunk is being filled.
*/
private float minY;
/**
* Z coordinate of the minimum point forming the bounding box in a chunk
* of data. This value will be updated while the chunk is being filled.
*/
private float minZ;
/**
* X coordinate of the maximum point forming the bounding box in a chunk
* of data. This value will be updated while the chunk is being filled.
*/
private float maxX;
/**
* Y coordinate of the maximum point forming the bounding box in a chunk
* of data. This value will be updated while the chunk is being filled.
*/
private float maxY;
/**
* Z coordinate of the maximum point forming the bounding box in a chunk
* of data. This value will be updated while the chunk is being filled.
*/
private float maxZ;
/**
* Reference to the listener of this loader iterator. This listener will
* be notified when the loading process finishes so that resources can
* be freed.
*/
private LoaderIteratorListener listener;
/**
* Array containing vertices coordinates to be added to current chunk
* of data.
*/
private float[] coordsInChunkArray;
/**
* Array containing normal coordinates to be added to current chunk of
* data.
*/
private float[] normalsInChunkArray;
/**
* Array containing indices to be added to current chunk of data. Notice
* that these indices are not the original indices appearing in the file.
* Instead, they are indices referring to data in current chunk,
* accounting for duplicate points, etc. This way, indices in a chunk
* can be directly used to draw the chunk of data by the graphical layer.
*/
private int[] indicesInChunkArray;
/**
* Number of vertices stored in chunk.
*/
private int verticesInChunk;
/**
* Number of indices stored in chunk.
*/
private int indicesInChunk;
/**
* Size of indices stored in chunk.
*/
private int indicesInChunkSize;
/**
* Indicates if file is in ASCII format or in binary format.
*/
private boolean isAscii;
/**
* Indicates number of triangles that are contained in the file (when
* available).
*/
private long numberOfTriangles;
/**
* Indicates current triangle being read (when available).
*/
private long currentTriangle;
/**
* Contains number of vertices contained in the file.
*/
private long numberOfVertices;
/**
* Indicates when end of file has been reached.
*/
private boolean endOfFileReached;
/**
* Contains name for the 3D object.
*/
private String solidName;
/**
* Constant defining beginning of 3D file.
*/
public static final String ASCII_START = "solid";
/**
* Constant defining end of 3D file.
*/
public static final String ASCII_END = "endsolid";
/**
* Constant defining a face (i.e. triangle or polygon).
*/
public static final String ASCII_FACET = "facet";
/**
* Constant defining a normal.
*/
public static final String ASCII_NORMAL = "normal";
/**
* Constant defining grouping levels.
*/
public static final String ASCII_OUTER = "outer";
/**
* Constant defining a loop that will contain vertices.
*/
public static final String ASCII_LOOP = "loop";
/**
* Constant defining a vertex.
*/
public static final String ASCII_VERTEX = "vertex";
/**
* Constant defining end of loop containing vertices.
*/
public static final String ASCII_END_LOOP = "endloop";
/**
* Constant defining end of 3D face (i.e. triangle or polygon).
*/
public static final String ASCII_END_FACET = "endfacet";
/**
* Header size for binary format.
*/
public static final int BINARY_HEADER_SIZE = 80;
/**
* Constant defining number of vertices in a triangle.
*/
public static final int VERTICES_PER_TRIANGLE = 3;
/**
* Constructor.
*
* @param loader reference to loader loading binary file.
* @throws IOException if an I/O error occurs.
* @throws LoaderException if file data is corrupt or cannot be
* understood.
*/
public LoaderIteratorSTL(final LoaderSTL loader) throws IOException, LoaderException {
this.loader = loader;
nX = nY = nZ = 1.0f;
listener = null;
coordsInChunkArray = null;
normalsInChunkArray = null;
indicesInChunkArray = null;
verticesInChunk = indicesInChunk = indicesInChunkSize = 0;
minX = minY = minZ = Float.MAX_VALUE;
maxX = maxY = maxZ = -Float.MAX_VALUE;
isAscii = false;
numberOfTriangles = currentTriangle = 0;
endOfFileReached = false;
numberOfVertices = 0;
setUp();
}
/**
* Method to set listener of this loader iterator.
* This listener will be notified when the loading process finishes.
*
* @param listener listener of this loader iterator.
*/
public void setListener(final LoaderIteratorListener listener) {
this.listener = listener;
}
/**
* Indicates if there is another chunk of data to be loaded.
*
* @return true if there is another chunk of data, false otherwise.
*/
@Override
public boolean hasNext() {
if (isAscii) {
return !endOfFileReached;
} else {
return currentTriangle < numberOfTriangles;
}
}
/**
* Loads and returns next chunk of data, if available.
*
* @return next chunk of data.
* @throws NotAvailableException thrown if no more data is available.
* @throws LoaderException if file data is corrupt or cannot be
* understood.
* @throws IOException if an I/O error occurs.
*/
@Override
public DataChunk next() throws NotAvailableException, LoaderException, IOException {
if (reader == null) {
throw new IOException();
}
if (!hasNext()) {
throw new NotAvailableException();
}
initChunkArrays();
// reset chunk bounding box values
minX = minY = minZ = Float.MAX_VALUE;
maxX = maxY = maxZ = -Float.MAX_VALUE;
// read data until chunk is full
var endOfChunk = false;
final var fileLength = file.length();
final var progressStep = Math.max((long) (LoaderSTL.PROGRESS_DELTA * fileLength), 1);
var previousPos = 0L;
try {
if (isAscii) {
// ascii format
String word;
do {
// read facet
word = readNonEmptyWord();
if (word == null) {
// undefined word
throw new LoaderException();
}
if (word.equalsIgnoreCase(ASCII_FACET)) {
// read normal
word = readNonEmptyWord();
if (word == null) {
// undefined word
throw new LoaderException();
}
if (word.equalsIgnoreCase(ASCII_NORMAL)) {
// read 3 normal values
word = readNonEmptyWord();
nX = Float.parseFloat(word);
word = readNonEmptyWord();
nY = Float.parseFloat(word);
word = readNonEmptyWord();
nZ = Float.parseFloat(word);
} else {
// unexpected word
throw new LoaderException();
}
} else if (word.equalsIgnoreCase(ASCII_OUTER)) {
// next word has to be "loop"
word = readNonEmptyWord();
if (word == null) {
// undefined word
throw new LoaderException();
}
if (!word.equalsIgnoreCase(ASCII_LOOP)) {
// unexpected word
throw new LoaderException();
}
} else if (word.equalsIgnoreCase(ASCII_VERTEX)) {
// read vertex data
word = readNonEmptyWord();
coordX = Float.parseFloat(word);
word = readNonEmptyWord();
coordY = Float.parseFloat(word);
word = readNonEmptyWord();
coordZ = Float.parseFloat(word);
// add coordinates into chunk arrays
addNewVertexDataToChunk();
// check if chunk is full
if (verticesInChunk == loader.maxVerticesInChunk) {
// no more vertices can be added to this chunk
break;
}
} else if (word.equalsIgnoreCase(ASCII_END_LOOP)) {
// check if chunk is full
if (verticesInChunk + VERTICES_PER_TRIANGLE >= loader.maxVerticesInChunk) {
// no more triangles vertices can be added to
// this chunk
break;
}
} else if (word.equalsIgnoreCase(ASCII_END_FACET)) {
// check if chunk is full
if (verticesInChunk + VERTICES_PER_TRIANGLE >= loader.maxVerticesInChunk) {
// no more triangles vertices can be added to
// this chunk
break;
}
} else if (word.equalsIgnoreCase(ASCII_END)) {
endOfFileReached = true;
break;
} else {
// unexpected word
throw new LoaderException();
}
// compute progress
if ((loader.listener != null) && (reader.getPosition() - previousPos) >= progressStep) {
previousPos = reader.getPosition();
loader.listener.onLoadProgressChange(loader,
(float) (reader.getPosition()) / fileLength);
}
} while (!endOfFileReached);
} else {
do {
// read vertex data (triangle normal, triangle vertices
// and two bytes attribute byte count
// read vertex normals
nX = reader.readFloat(EndianType.LITTLE_ENDIAN_TYPE);
nY = reader.readFloat(EndianType.LITTLE_ENDIAN_TYPE);
nZ = reader.readFloat(EndianType.LITTLE_ENDIAN_TYPE);
// read 1st vertex coordinates of triangle
coordX = reader.readFloat(EndianType.LITTLE_ENDIAN_TYPE);
coordY = reader.readFloat(EndianType.LITTLE_ENDIAN_TYPE);
coordZ = reader.readFloat(EndianType.LITTLE_ENDIAN_TYPE);
// add coordinates into chunk arrays
addNewVertexDataToChunk();
// read 2nd vertex coordinates of triangle
coordX = reader.readFloat(EndianType.LITTLE_ENDIAN_TYPE);
coordY = reader.readFloat(EndianType.LITTLE_ENDIAN_TYPE);
coordZ = reader.readFloat(EndianType.LITTLE_ENDIAN_TYPE);
// add coordinates into chunk arrays
addNewVertexDataToChunk();
// read 3rd vertex coordinates of triangle
coordX = reader.readFloat(EndianType.LITTLE_ENDIAN_TYPE);
coordY = reader.readFloat(EndianType.LITTLE_ENDIAN_TYPE);
coordZ = reader.readFloat(EndianType.LITTLE_ENDIAN_TYPE);
// add coordinates into chunk arrays
addNewVertexDataToChunk();
// read two bytes attribute byte count
reader.readUnsignedShort(EndianType.LITTLE_ENDIAN_TYPE);
currentTriangle++;
// compute progress
if ((loader.listener != null) && (reader.getPosition() - previousPos) >= progressStep) {
previousPos = reader.getPosition();
loader.listener.onLoadProgressChange(loader,
(float) (reader.getPosition()) / fileLength);
}
if (verticesInChunk + VERTICES_PER_TRIANGLE >= loader.maxVerticesInChunk) {
// no more triangles vertices can be added to this
// chunk
endOfChunk = true;
break;
}
} while (!reader.isEndOfStream() && currentTriangle < numberOfTriangles);
// file ended before all triangles were read
if (currentTriangle < numberOfTriangles && !endOfChunk) {
throw new LoaderException();
}
}
} catch (final IOException | LoaderException e) {
throw e;
} catch (final Exception e) {
throw new LoaderException(e);
}
// trim arrays to store only needed data
trimArrays();
// Instantiate DataChunk with chunk arrays
final var dataChunk = new DataChunk();
dataChunk.setVerticesCoordinatesData(coordsInChunkArray);
dataChunk.setMinX(minX);
dataChunk.setMinY(minY);
dataChunk.setMinZ(minZ);
dataChunk.setMaxX(maxX);
dataChunk.setMaxY(maxY);
dataChunk.setMaxZ(maxZ);
dataChunk.setIndicesData(indicesInChunkArray);
dataChunk.setNormalsData(normalsInChunkArray);
if (!hasNext() && listener != null) {
// notify iterator finished
listener.onIteratorFinished(this);
}
// if no more chunks are available, then close input reader
if (!hasNext()) {
reader.close();
}
return dataChunk;
}
/**
* Returns name for the 3D object.
*
* @return name for the 3D object.
*/
public String getSolidName() {
return solidName;
}
/**
* Gets number of vertices contained in the file.
*
* @return number of vertices contained in the file.
*/
public long getNumberOfVertices() {
return numberOfVertices;
}
/**
* Internal method to read a word from ASCII file.
*
* @return next word that has been read.
* @throws IOException if an I/O error occurs.
*/
private String readNonEmptyWord() throws IOException {
// read normal
String word;
do {
// ignore empty words
// (line feeds, etc.)
word = reader.readWord();
} while (word != null && word.isEmpty());
return word;
}
/**
* Initializes arrays forming current chunk of data.
*/
private void initChunkArrays() {
coordsInChunkArray = new float[loader.maxVerticesInChunk * VERTICES_PER_TRIANGLE];
normalsInChunkArray = new float[loader.maxVerticesInChunk * VERTICES_PER_TRIANGLE];
indicesInChunkArray = new int[loader.maxVerticesInChunk];
verticesInChunk = indicesInChunk = 0;
indicesInChunkSize = loader.maxVerticesInChunk;
}
/**
* Adds data of last vertex being loaded to current chunk of data as a
* new vertex.
*/
private void addNewVertexDataToChunk() {
var pos = 3 * verticesInChunk;
coordsInChunkArray[pos] = coordX;
normalsInChunkArray[pos] = nX;
pos++;
coordsInChunkArray[pos] = coordY;
normalsInChunkArray[pos] = nY;
pos++;
coordsInChunkArray[pos] = coordZ;
normalsInChunkArray[pos] = nZ;
// update bounding box values
if (coordX < minX) {
minX = coordX;
}
if (coordY < minY) {
minY = coordY;
}
if (coordZ < minZ) {
minZ = coordZ;
}
if (coordX > maxX) {
maxX = coordX;
}
if (coordY > maxY) {
maxY = coordY;
}
if (coordZ > maxZ) {
maxZ = coordZ;
}
// if arrays of indices become full, we need to resize them
if (indicesInChunk >= indicesInChunkSize) {
increaseIndicesArraySize();
}
indicesInChunkArray[indicesInChunk] = verticesInChunk;
verticesInChunk++;
indicesInChunk++;
}
/**
* Increases size of arrays of data. This method is called when needed.
*/
private void increaseIndicesArraySize() {
final var newIndicesInChunkSize = indicesInChunkSize + loader.maxVerticesInChunk;
final var newIndicesInChunkArray = new int[newIndicesInChunkSize];
// copy contents of old array
System.arraycopy(indicesInChunkArray, 0, newIndicesInChunkArray, 0, indicesInChunkSize);
// set new arrays and new size
indicesInChunkArray = newIndicesInChunkArray;
indicesInChunkSize = newIndicesInChunkSize;
}
/**
* Trims arrays of data to reduce size of arrays to fit chunk data. This
* method is loaded just before copying data to chunk being returned.
*/
private void trimArrays() {
if (verticesInChunk > 0) {
final var elems = verticesInChunk * VERTICES_PER_TRIANGLE;
final var newCoordsInChunkArray = new float[elems];
final var newNormalsInChunkArray = new float[elems];
// copy contents of old arrays
System.arraycopy(coordsInChunkArray, 0, newCoordsInChunkArray, 0, elems);
System.arraycopy(normalsInChunkArray, 0, newNormalsInChunkArray, 0, elems);
// set new arrays
coordsInChunkArray = newCoordsInChunkArray;
normalsInChunkArray = newNormalsInChunkArray;
} else {
// allow garbage collection
coordsInChunkArray = null;
normalsInChunkArray = null;
}
if (indicesInChunk > 0) {
final var newIndicesInChunkArray = new int[indicesInChunk];
System.arraycopy(indicesInChunkArray, 0, newIndicesInChunkArray, 0, indicesInChunk);
// set new array
indicesInChunkArray = newIndicesInChunkArray;
} else {
// allow garbage collection
indicesInChunkArray = null;
}
}
/**
* Setups loader iterator. This method is called when constructing
* this iterator.
*
* @throws IOException if an I/O error occurs.
* @throws LoaderException if data is corrupted or cannot be understood.
*/
private void setUp() throws IOException, LoaderException {
// read start of file to determine whether it is in ascii or binary
// format
// move to start of file
reader.seek(0);
final var magicStartLength = ASCII_START.length();
final var buffer = new byte[magicStartLength];
final var n = reader.read(buffer);
if (n != magicStartLength) {
throw new LoaderException();
}
final var str = new String(buffer);
isAscii = str.equalsIgnoreCase(ASCII_START);
if (isAscii) {
// check
if (reader.isEndOfStream()) {
throw new LoaderException();
}
// load solid name
solidName = reader.readLine();
} else {
// Binary format (always is in little endian form)
// set position after 80 byte header
reader.seek(BINARY_HEADER_SIZE);
// read number of triangles
numberOfTriangles = reader.readUnsignedInt(EndianType.LITTLE_ENDIAN_TYPE);
numberOfVertices = VERTICES_PER_TRIANGLE * numberOfTriangles;
}
}
}
}