aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlDataStore.java29
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlGeometryStore.java120
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlImporter.java556
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/FileHelper.java29
4 files changed, 734 insertions, 0 deletions
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlDataStore.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlDataStore.java
new file mode 100644
index 0000000..6c5ce48
--- /dev/null
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlDataStore.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.stl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.ardor3d.math.Vector3;
+
+public class StlDataStore {
+ private final List<Vector3> _vertices = new ArrayList<>();
+ private final List<Vector3> _normals = new ArrayList<>();
+
+ public List<Vector3> getVertices() {
+ return _vertices;
+ }
+
+ public List<Vector3> getNormals() {
+ return _normals;
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlGeometryStore.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlGeometryStore.java
new file mode 100644
index 0000000..0b33e9d
--- /dev/null
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlGeometryStore.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.stl;
+
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+import com.ardor3d.math.ColorRGBA;
+import com.ardor3d.math.Vector3;
+import com.ardor3d.math.type.ReadOnlyColorRGBA;
+import com.ardor3d.scenegraph.Mesh;
+import com.ardor3d.scenegraph.MeshData;
+import com.ardor3d.scenegraph.Node;
+import com.ardor3d.util.geom.BufferUtils;
+
+public class StlGeometryStore {
+
+ private final StlDataStore _dataStore;
+
+ private final Node _root;
+
+ private String _currentObjectName;
+
+ public StlGeometryStore() {
+ super();
+ _dataStore = new StlDataStore();
+ _root = new Node();
+ }
+
+ public StlDataStore getDataStore() {
+ return _dataStore;
+ }
+
+ public Node getScene() {
+ return _root;
+ }
+
+ void setCurrentObjectName(final String name) {
+ commitObjects();
+ _currentObjectName = name;
+ }
+
+ void commitObjects() {
+ if (!_dataStore.getNormals().isEmpty()) {
+ final String name;
+ if (_currentObjectName == null) {
+ name = "stl_mesh";
+ } else {
+ name = _currentObjectName;
+ }
+
+ // mesh object to return
+ final Mesh mesh = new Mesh(name);
+ final MeshData meshData = mesh.getMeshData();
+
+ // allocate buffers
+ final int numberTriangles = _dataStore.getNormals().size();
+ final int vertexBufferSize = 3 * numberTriangles;
+ final FloatBuffer vertexBuffer = BufferUtils.createVector3Buffer(vertexBufferSize);
+ final FloatBuffer normalBuffer = BufferUtils.createVector3Buffer(vertexBufferSize);
+ final FloatBuffer colourBuffer = BufferUtils.createColorBuffer(vertexBufferSize);
+
+ // fill buffers
+ int vertexCount = 0;
+ int normalCount = 0;
+ int colourCount = 0;
+ Vector3 v0;
+ Vector3 v1;
+ Vector3 v2;
+ Vector3 n;
+ final ReadOnlyColorRGBA defaultColour = ColorRGBA.WHITE;
+ for (int i = 0; i < numberTriangles; i++) {
+ // triangle properties
+ v0 = _dataStore.getVertices().get(3 * i + 0);
+ v1 = _dataStore.getVertices().get(3 * i + 1);
+ v2 = _dataStore.getVertices().get(3 * i + 2);
+ n = _dataStore.getNormals().get(i);
+ // vertices
+ BufferUtils.setInBuffer(v0, vertexBuffer, vertexCount++);
+ BufferUtils.setInBuffer(v1, vertexBuffer, vertexCount++);
+ BufferUtils.setInBuffer(v2, vertexBuffer, vertexCount++);
+ // normals - 1 foreach vertex
+ BufferUtils.setInBuffer(n, normalBuffer, normalCount++);
+ BufferUtils.setInBuffer(n, normalBuffer, normalCount++);
+ BufferUtils.setInBuffer(n, normalBuffer, normalCount++);
+ // colours - 1 foreach vertex
+ BufferUtils.setInBuffer(defaultColour, colourBuffer, colourCount++);
+ BufferUtils.setInBuffer(defaultColour, colourBuffer, colourCount++);
+ BufferUtils.setInBuffer(defaultColour, colourBuffer, colourCount++);
+ }
+
+ meshData.setVertexBuffer(vertexBuffer);
+ meshData.setNormalBuffer(normalBuffer);
+ meshData.setColorBuffer(colourBuffer);
+
+ // indices buffer
+ final int[] indices = new int[vertexBufferSize];
+ for (int i = 0; i < vertexBufferSize; i++) {
+ indices[i] = i;
+ }
+ final IntBuffer iBuffer = BufferUtils.createIntBuffer(indices.length);
+ iBuffer.put(indices);
+ meshData.setIndexBuffer(iBuffer);
+
+ _root.attachChild(mesh);
+ }
+ }
+
+ void cleanup() {
+ _currentObjectName = null;
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlImporter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlImporter.java
new file mode 100644
index 0000000..dbe2cdb
--- /dev/null
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlImporter.java
@@ -0,0 +1,556 @@
+/**
+ * Copyright (c) 2008-2016 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.stl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StreamTokenizer;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.ardor3d.extension.model.util.FileHelper;
+import com.ardor3d.math.Vector3;
+import com.ardor3d.util.resource.ResourceLocator;
+import com.ardor3d.util.resource.ResourceLocatorTool;
+import com.ardor3d.util.resource.ResourceSource;
+
+/**
+ * Reads an STL (STereoLithography) file and builds an Ardor3D Mesh. The STL format consists entirely of triangles and
+ * as a result is a simple format to handle. Also, it is widely supported by the CAD/CAM community.
+ *
+ * This class supports both ASCII and Binary formats and files residing either locally or on a network.
+ *
+ * Refer to <a href="http://en.wikipedia.org/wiki/STL_(file_format)" target="_blank>Wikipedia</a>. Several STL models
+ * can be downloaded freely from <a href="http://grabcad.com" target="_blank">GrabCAD</a>.
+ *
+ * @author gmseed
+ * @see StlFileParser
+ */
+public class StlImporter {
+
+ /**
+ * Extends StreamTokenizer for parsing STL files. The STL format for Ascii files is as follows:
+ *
+ * <pre>
+ * solid name
+ * ...
+ * facet normal ni nj nk
+ * outer loop
+ * vertex v1x v1y v1z
+ * vertex v2x v2y v2z
+ * vertex v3x v3y v3z
+ * endloop
+ * endfacet
+ * ...
+ * endsolid name
+ * </pre>
+ *
+ * @author gmseed
+ */
+ public static class StlFileParser extends StreamTokenizer {
+
+ /**
+ * Constructor.
+ *
+ * @param reader
+ * The Reader.
+ */
+ public StlFileParser(final Reader reader) {
+ super(reader);
+ resetSyntax();
+ eolIsSignificant(true);
+ lowerCaseMode(true);
+
+ // all printable ascii characters
+ wordChars('!', '~');
+
+ whitespaceChars(' ', ' ');
+ whitespaceChars('\n', '\n');
+ whitespaceChars('\r', '\r');
+ whitespaceChars('\t', '\t');
+ }
+
+ /**
+ * Gets a number from the stream. Need to extract numbers since they may be in scientific notation. The number
+ * is returned in nval.
+ *
+ * @return Logical-true if successful, else logical-false.
+ */
+ protected boolean getNumber() {
+ try {
+ nextToken();
+ if (ttype != StreamTokenizer.TT_WORD) {
+ return false;
+ }
+ nval = Double.valueOf(sval).doubleValue();
+ } catch (final IOException e) {
+ System.err.println(e.getMessage());
+ return false;
+ } catch (final NumberFormatException e) {
+ System.err.println(e.getMessage());
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+ // logger
+ private static final Logger LOGGER = Logger.getLogger(StlImporter.class.getName());
+
+ private ResourceLocator _modelLocator;
+
+ /**
+ * Constructor.
+ */
+ public StlImporter() {
+ super();
+ }
+
+ /**
+ * Reads a STL file from the given resource
+ *
+ * @param resource
+ * the name of the resource to find.
+ * @return a StlGeometryStore data object containing the scene and other useful elements.
+ */
+ public StlGeometryStore load(final String resource) {
+ return load(resource, new FileHelper());
+ }
+
+ /**
+ * Reads a STL file from the given resource
+ *
+ * @param resource
+ * the name of the resource to find.
+ * @param fileHelper
+ * the file helper used to determine whether the resource is an Ascii file or a binary file
+ *
+ * @return a StlGeometryStore data object containing the scene and other useful elements.
+ */
+ public StlGeometryStore load(final String resource, final FileHelper fileHelper) {
+ final ResourceSource source;
+ if (_modelLocator == null) {
+ source = ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_MODEL, resource);
+ } else {
+ source = _modelLocator.locateResource(resource);
+ }
+
+ if (source == null) {
+ throw new Error("Unable to locate '" + resource + "'");
+ }
+
+ return load(source, fileHelper);
+ }
+
+ /**
+ * Reads a STL file from the given resource
+ *
+ * @param resource
+ * the resource to find.
+ * @return a StlGeometryStore data object containing the scene and other useful elements.
+ */
+ public StlGeometryStore load(final ResourceSource resource) {
+ return load(resource, new FileHelper());
+ }
+
+ /**
+ * Reads a STL file from the given resource
+ *
+ * @param resource
+ * the resource to find.
+ * @param fileHelper
+ * the file helper used to determine whether the resource is an Ascii file or a binary file
+ *
+ * @return a StlGeometryStore data object containing the scene and other useful elements.
+ */
+ public StlGeometryStore load(final ResourceSource resource, final FileHelper fileHelper) {
+ final boolean isAscii = fileHelper.isFilePureAscii(resource);
+ final StlGeometryStore store = new StlGeometryStore();
+ if (isAscii) { // Ascii file
+ try (final BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream()))) {
+ final StlFileParser parser = new StlFileParser(reader);
+ try {
+ parser.nextToken();
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE, "IO Error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ // read "solid"
+ if (!parser.sval.equals("solid")) {
+ StlImporter.LOGGER.log(Level.SEVERE, "Ascii file but no solid keyword");
+ }
+ try {
+ parser.nextToken();
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE, "IO Error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ // read object name if any
+ if (parser.ttype != StreamTokenizer.TT_WORD) {
+ StlImporter.LOGGER.log(Level.WARNING,
+ "Format Warning: expecting the object name on line " + parser.lineno());
+ } else {
+ final String objectName = parser.sval;
+ store.setCurrentObjectName(objectName);
+ // Reads the EOL for verifying that the file has a correct format
+ try {
+ parser.nextToken();
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "IO Error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ if (parser.ttype != StreamTokenizer.TT_EOL) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting End Of Line on line " + parser.lineno());
+ }
+ }
+ // read the rest of the file
+ try {
+ parser.nextToken();
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE, "IO Error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ // read all the facets
+ while (parser.ttype != StreamTokenizer.TT_EOF && !parser.sval.equals("endsolid")) {
+ // Reads "facet"
+ if (!(parser.ttype == StreamTokenizer.TT_WORD && parser.sval.equals("facet"))) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error:expecting 'facet' on line " + parser.lineno());
+ } else {
+ try {
+ parser.nextToken();
+ // Reads a normal
+ if (!(parser.ttype == StreamTokenizer.TT_WORD && parser.sval.equals("normal"))) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error:expecting 'normal' on line " + parser.lineno());
+ } else {
+ if (parser.getNumber()) {
+ final Vector3 normal = new Vector3();
+ normal.setX(parser.nval);
+
+ if (parser.getNumber()) {
+ normal.setY(parser.nval);
+
+ if (parser.getNumber()) {
+ normal.setZ(parser.nval);
+
+ store.getDataStore().getNormals().add(normal);
+ // Reads the EOL for verifying that the file has a correct format
+ try {
+ parser.nextToken();
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "IO Error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ if (parser.ttype != StreamTokenizer.TT_EOL) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting End Of Line on line "
+ + parser.lineno());
+ }
+ } else {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting normal z-component on line "
+ + parser.lineno());
+ }
+ } else {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting normal y-component on line "
+ + parser.lineno());
+ }
+ } else {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting normal x-component on line " + parser.lineno());
+ }
+ }
+
+ parser.nextToken();
+ // Reads "outer loop" then EOL
+ if (!(parser.ttype == StreamTokenizer.TT_WORD && parser.sval.equals("outer"))) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting 'outer' on line " + parser.lineno());
+ } else {
+ try {
+ parser.nextToken();
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "IO error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ if (!(parser.ttype == StreamTokenizer.TT_WORD && parser.sval.equals("loop"))) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error:expecting 'loop' on line " + parser.lineno());
+ } else {
+ // Reads the EOL for verifying that the file has a correct format
+ try {
+ parser.nextToken();
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "IO Error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ if (parser.ttype != StreamTokenizer.TT_EOL) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting End Of Line on line " + parser.lineno());
+ }
+ }
+ }
+
+ parser.nextToken();
+ // Reads the first vertex
+ if (!(parser.ttype == StreamTokenizer.TT_WORD && parser.sval.equals("vertex"))) {
+ System.err.println("Format Error:expecting 'vertex' on line " + parser.lineno());
+ } else {
+ if (parser.getNumber()) {
+ final Vector3 vertex = new Vector3();
+ vertex.setX(parser.nval);
+
+ if (parser.getNumber()) {
+ vertex.setY(parser.nval);
+
+ if (parser.getNumber()) {
+ vertex.setZ(parser.nval);
+
+ store.getDataStore().getVertices().add(vertex);
+ // Reads the EOL for verifying that the file has a correct format
+ try {
+ parser.nextToken();
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "IO Error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ if (parser.ttype != StreamTokenizer.TT_EOL) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting End Of Line on line "
+ + parser.lineno());
+ }
+ } else {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting vertex z-component on line "
+ + parser.lineno());
+ }
+ } else {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting vertex y-component on line "
+ + parser.lineno());
+ }
+ } else {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting vertex x-component on line " + parser.lineno());
+ }
+ }
+
+ parser.nextToken();
+ // Reads the second vertex
+ if (!(parser.ttype == StreamTokenizer.TT_WORD && parser.sval.equals("vertex"))) {
+ System.err.println("Format Error:expecting 'vertex' on line " + parser.lineno());
+ } else {
+ if (parser.getNumber()) {
+ final Vector3 vertex = new Vector3();
+ vertex.setX(parser.nval);
+
+ if (parser.getNumber()) {
+ vertex.setY(parser.nval);
+
+ if (parser.getNumber()) {
+ vertex.setZ(parser.nval);
+
+ store.getDataStore().getVertices().add(vertex);
+ // Reads the EOL for verifying that the file has a correct format
+ try {
+ parser.nextToken();
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "IO Error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ if (parser.ttype != StreamTokenizer.TT_EOL) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting End Of Line on line "
+ + parser.lineno());
+ }
+ } else {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting vertex z-component on line "
+ + parser.lineno());
+ }
+ } else {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting vertex y-component on line "
+ + parser.lineno());
+ }
+ } else {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting vertex x-component on line " + parser.lineno());
+ }
+ }
+
+ parser.nextToken();
+ // Reads the third vertex
+ if (!(parser.ttype == StreamTokenizer.TT_WORD && parser.sval.equals("vertex"))) {
+ System.err.println("Format Error:expecting 'vertex' on line " + parser.lineno());
+ } else {
+ if (parser.getNumber()) {
+ final Vector3 vertex = new Vector3();
+ vertex.setX(parser.nval);
+
+ if (parser.getNumber()) {
+ vertex.setY(parser.nval);
+
+ if (parser.getNumber()) {
+ vertex.setZ(parser.nval);
+
+ store.getDataStore().getVertices().add(vertex);
+ // Reads the EOL for verifying that the file has a correct format
+ try {
+ parser.nextToken();
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "IO Error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ if (parser.ttype != StreamTokenizer.TT_EOL) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting End Of Line on line "
+ + parser.lineno());
+ }
+ } else {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting vertex z-component on line "
+ + parser.lineno());
+ }
+ } else {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting vertex y-component on line "
+ + parser.lineno());
+ }
+ } else {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting vertex x-component on line " + parser.lineno());
+ }
+ }
+
+ parser.nextToken();
+ // Reads "endloop" then EOL
+ if (!(parser.ttype == StreamTokenizer.TT_WORD && parser.sval.equals("endloop"))) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting 'endloop' on line " + parser.lineno());
+ } else {
+ // Reads the EOL for verifying that the file has a correct format
+ try {
+ parser.nextToken();
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "IO Error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ if (parser.ttype != StreamTokenizer.TT_EOL) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting End Of Line on line " + parser.lineno());
+ }
+ }
+
+ parser.nextToken();
+ // Reads "endfacet" then EOL
+ if (!(parser.ttype == StreamTokenizer.TT_WORD && parser.sval.equals("endfacet"))) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error:expecting 'endfacet' on line " + parser.lineno());
+ } else {
+ // Reads the EOL for verifying that the file has a correct format
+ try {
+ parser.nextToken();
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "IO Error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ if (parser.ttype != StreamTokenizer.TT_EOL) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "Format Error: expecting End Of Line on line " + parser.lineno());
+ }
+ }
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "IO Error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ }
+ try {
+ parser.nextToken();
+ } catch (final IOException e) {
+ StlImporter.LOGGER.log(Level.SEVERE,
+ "IO Error on line " + parser.lineno() + ": " + e.getMessage());
+ }
+ }
+ } catch (final Throwable t) {
+ throw new Error("Unable to load stl resource from URL: " + resource, t);
+ }
+ } else { // Binary file
+ try (final InputStream data = resource.openStream()) {
+ ByteBuffer dataBuffer; // To read in the correct endianness
+ final byte[] info = new byte[80]; // Header data
+ final byte[] numberFaces = new byte[4]; // the number of faces
+ byte[] faceData; // face data
+ int numberTriangles; // First info (after the header) on the file
+
+ // the first 80 bytes aren't important (except if you want to support non standard colors)
+ if (80 != data.read(info)) {
+ throw new IOException("Format Error: 80 bytes expected");
+ } else {
+ // read number of faces, setting the correct order
+ data.read(numberFaces);
+ dataBuffer = ByteBuffer.wrap(numberFaces);
+ dataBuffer.order(ByteOrder.nativeOrder());
+
+ // allocate buffer for face data, with each face requiring 50 bytes
+ numberTriangles = dataBuffer.getInt();
+ faceData = new byte[50 * numberTriangles];
+
+ // read face data
+ data.read(faceData);
+ dataBuffer = ByteBuffer.wrap(faceData);
+ dataBuffer.order(ByteOrder.nativeOrder());
+
+ // read each facet noting that after each fact there are 2 bytes without information
+ // no need to skip for last iteration
+ for (int index = 0; index < numberTriangles; index++) {
+ try {
+ // Reads a facet from a binary file.
+ // normal
+ store.getDataStore().getNormals().add(
+ new Vector3(dataBuffer.getFloat(), dataBuffer.getFloat(), dataBuffer.getFloat()));
+ // 3 vertices
+ store.getDataStore().getVertices().add(
+ new Vector3(dataBuffer.getFloat(), dataBuffer.getFloat(), dataBuffer.getFloat()));
+ store.getDataStore().getVertices().add(
+ new Vector3(dataBuffer.getFloat(), dataBuffer.getFloat(), dataBuffer.getFloat()));
+ store.getDataStore().getVertices().add(
+ new Vector3(dataBuffer.getFloat(), dataBuffer.getFloat(), dataBuffer.getFloat()));
+ if (index != numberTriangles - 1) {
+ dataBuffer.get();
+ dataBuffer.get();
+ }
+ } catch (final Throwable t) {
+ throw new Exception("Format Error: iteration number " + index, t);
+ }
+ }
+ }
+ } catch (final Throwable t) {
+ throw new Error("Unable to load stl resource from URL: " + resource, t);
+ }
+ }
+
+ store.commitObjects();
+ store.cleanup();
+ return store;
+
+ }
+
+ public void setModelLocator(final ResourceLocator locator) {
+ _modelLocator = locator;
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/FileHelper.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/FileHelper.java
index 5f8ae2b..8a9c5cf 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/FileHelper.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/FileHelper.java
@@ -13,6 +13,7 @@ package com.ardor3d.extension.model.util;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
@@ -20,6 +21,8 @@ import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.StandardCharsets;
+import com.ardor3d.util.resource.ResourceSource;
+
public class FileHelper {
/**
@@ -71,4 +74,30 @@ public class FileHelper {
}
return true;
}
+
+ /**
+ * Tests whether or not the file with the specified filename is pure ASCII. The method used is to read the file a
+ * line at a time and test if each line is ASCII.
+ *
+ * @param resource
+ * the name of the resource to find.
+ * @return Logical-true if pure ASCII, else logical-false.
+ */
+ public boolean isFilePureAscii(final ResourceSource resource) {
+ try (final InputStream inputStream = resource.openStream();
+ final DataInputStream in = new DataInputStream(inputStream);
+ final BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
+ String strLine;
+ // read file a line at a time
+ while ((strLine = br.readLine()) != null) {
+ final boolean isAscii = isStringPureAscii(strLine);
+ if (!isAscii) {
+ return false;
+ }
+ }
+ } catch (final Exception e) {
+ return false;
+ }
+ return true;
+ }
}