All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jeometry.io.ply.PLYReader Maven / Gradle / Ivy

There is a newer version: 1.0.5
Show newest version
package org.jeometry.io.ply;

import static org.jeometry.Jeometry.logger; 

import java.awt.Color;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.jeometry.Jeometry;
import org.jeometry.factory.JeometryFactory;
import org.jeometry.geom2D.point.Point2D;
import org.jeometry.geom2D.point.Point2DContainer;
import org.jeometry.geom3D.mesh.Face;
import org.jeometry.geom3D.mesh.Mesh;
import org.jeometry.geom3D.mesh.indexed.IndexedMesh;
import org.jeometry.geom3D.mesh.indexed.IndexedTriangleMesh;
import org.jeometry.geom3D.point.Point3D;
import org.jeometry.geom3D.point.Point3DContainer;
import org.jeometry.geom3D.primitive.Triangle;
import org.jeometry.geom3D.properties.Colored;
import org.jeometry.geom3D.properties.HasNormal;
import org.jeometry.geom3D.textured.Texturable;
import org.jeometry.geom3D.textured.Texture;
import org.jeometry.geom3D.textured.TextureManager;
import org.jeometry.geom3D.textured.TexturePath;
import org.jeometry.math.Vector;
import org.jorigin.identification.Identified;
import org.jorigin.identification.Named;
import org.jorigin.io.IOStreamUtil;
import org.jorigin.lang.PathUtil;
import org.jorigin.property.HandleUserProperties;

/**
 * This class enable to read a geometric object from a PLY file. Due to the data
 * contained within the file, the nature of the object can be:
 * 
    *
  • {@link Point3DContainer 3D point container} if the PLY file only contains 3D vertex information; *
  • {@link Point2DContainer 2D point container} if the PLY file only contains 2D vertex information; *
  • {@link IndexedMesh Indexed mesh} if the PLY file contains vertex and faces information; *
  • {@link IndexedTriangleMesh an indexed triangle mesh} if the PLY file contains vertex and faces information and if all faces are triangular; *
* The class used to wrap PLY vertex into point can be set by using {@link #setPoint3DClass(Class)} method. * @param the type of the underlying 3D points. It should be ensured that 3D point created from from {@link JeometryFactory geometry factory} delivers objects that are compatible with the type T * @author Julien Seinturier - COMEX S.A. - [email protected] - https://github.com/jorigin/jeometry * @version {@value Jeometry#version} * @since 1.0.0 */ public class PLYReader { /** * The standard name of a vertex. */ public static final String VERTEX_NAME = "VERTEX"; /** * The standard name of an edge. */ public static final String EDGE_NAME = "EDGE"; /** * The standard name of a face. */ public static final String FACE_NAME = "FACE"; private static final int GEOM_POINTS = 1; private static final int GEOM_POLYHEDRON = 2; private static final int X_INDEX = 0; private static final int Y_INDEX = 1; private static final int Z_INDEX = 2; private static final int R_INDEX = 3; private static final int G_INDEX = 4; private static final int B_INDEX = 5; private static final int A_INDEX = 6; private static final int NX_INDEX = 7; private static final int NY_INDEX = 8; private static final int NZ_INDEX = 9; private static final int VERTEX_LIST_INDEX = 10; private static final int TEX_INDEX = 11; private static final int TEX_COORDS_INDEX = 12; // private static final int ID_INDEX = 13; private static final int TEX_U_INDEX = 14; private static final int TEX_V_INDEX = 15; private String pointName = "PLY"; private final String plyXPropName = "x"; private final String plyYPropName = "y"; private final String plyZPropName = "z"; private final String plyRPropName = "red"; private final String plyGPropName = "green"; private final String plyBPropName = "blue"; private final String plyAPropName = "alpha"; private final String plyNxPropName = "nx"; private final String plyNyPropName = "ny"; private final String plyNzPropName = "nz"; private final String plyVertexListPropName = "vertex_indices"; private final String plyVertexListPropNameAlt = "vertex_index"; private final String plyTexCoordPropName = "texcoord"; private final String plyTexIndexPropName = "texnumber"; private final String plyVertexTexCoordUPropName = "texture_u"; private final String plyVertexTexCoordVPropName = "texture_v"; private final String plyIDPropName = "id"; private Collection listeners = null; private List textures = null; private boolean readPointColor = true; private boolean readPointNormal = true; private boolean identifyPoints = true; private int vertexCount = -1; private int faceCurrentIndex = -1; private final String faceGenericName = "F"; private final String faceTriangleName = "T"; // private final String vertexTriangleName = "V"; private String resourcePath = null; /** * The End Of Line (eol) character. By default, this character is obtained * from property line.separator of the Java Environment ( * {@link System#getProperty(String)}). */ private String eol = System.getProperty("line.separator"); /** * The charset used for low level data encoding. By default the charset is set * to UTF-8. * * @see #setCharset(String) * @see #getCharset() */ private String charset = "UTF-8"; /** * The separator between two fields within a PLY file. */ private final String fieldSeparator = " "; /** * The class used for instantiating vertices. */ private Class pointClass = null; /** * The class used for instantiating 2D points (texture, ...) */ private Class point2DClass = null; private int fileFormat = PLY.FILE_FORMAT_ASCII; private int geometryType = -1; private int[] propertyIndexes = null; private boolean flipTextureY = true; /** * Create a new default PLY reader. */ public PLYReader() { } /** * Get the format of the PLY file. There is 3 formats available: *
    *
  • {@link PLY#FILE_FORMAT_ASCII} for an ASCII file; *
  • {@link PLY#FILE_FORMAT_BINARY_LE} for a binary file with Little Endian coding;
  • *
  • {@link PLY#FILE_FORMAT_BINARY_BE} for a binary file with Big Endian coding;
  • *
* * @return the format of the PLY file. */ public int getFileFormat() { return this.fileFormat; } /** * Get the charset used for low level data encoding. By default the charset is * set to UTF-8. * @return the charset used for low level data encoding. * @see #setCharset(String) */ public String getCharset() { return charset; } /** * Set the charset used for low level data encoding. By default the charset is * set to UTF-8. * * @param charset the charset used for low level data encoding. * @see #getCharset() */ public void setCharset(String charset) { this.charset = charset; } /** * Get The end Of Line (eol) character. By default, this character is obtained * from property line.separator of the Java Environment ( * {@link System#getProperty(String)}). * * @return the end Of Line (eol) character. * @see #setEol(String) */ public String getEol() { return eol; } /** * Set The end Of Line (eol) character. By default, this character is obtained * from property line.separator of the Java Environment ({@link System#getProperty(String)}). * * @param eol the end Of Line (eol) character. * @see #getEol() */ public void setEol(String eol) { this.eol = eol; } /** * Get the class that has to be instantiated to represent 3D points. * The given pointClass is an implementation of {@link Point3D} interface. * If this method return null, points are instantiated through {@link JeometryFactory#createPoint3D() GeometryFactory.createPoint3D()} method. * * @return the class that has to be instantiated to represent the vertices. * @see #setPoint3DClass(Class) */ public Class getPoint3DClass() { return pointClass; } /** * Set the class that has to be instantiated to represent the vertices. * The given pointClass has to be an implementation of {@link Point3D} interface. * If this method return null, points are instantiated through {@link JeometryFactory#createPoint3D() GeometryFactory.createPoint3D()} method. * * @param pointClass the class that has to be instantiated to represent the vertices. * @see #getPoint3DClass() */ public void setPoint3DClass(Class pointClass) { this.pointClass = pointClass; } /** * Get the class that is instantiated to represent the 2D points (such as Texture coordinates). * The given pointClass has to be an implementation of {@link Point2D} interface. * @return the class that is instantiated to represent the 2D points (such as Texture coordinates). * @see #setPoint2DClass(Class) */ public Class getPoint2DClass() { return point2DClass; } /** * Set the class that has to be instantiated to represent the 2D points (such as Texture coordinates). * The given pointClass has to be an implementation of {@link Point2D} interface. * @param point2DClass the class that has to be instantiated to represent the 2D points (such as Texture coordinates). * @see #getPoint2DClass() */ public void setPoint2DClass(Class point2DClass) { this.point2DClass = point2DClass; } /** * Get if the PLY reader is taking take into account point colors during read. * @return true if the reader is taking take into account point colors during read or false otherwise. * @see #setReadPointColor(boolean) */ public boolean isReadPointColor() { return readPointColor; } /** * Set if the PLY reader has to take in account point colors during read. * @param readPointColor true if the reader has to take in account point colors during read or false otherwise. * @see #isReadPointColor() */ public void setReadPointColor(boolean readPointColor) { this.readPointColor = readPointColor; } /** * Get if the PLY reader is taking into account point normals during read. * @return true if the reader is taking into account point normals during read or false otherwise. * @see #setReadPointNormal(boolean) */ public boolean isReadPointNormal() { return readPointNormal; } /** * Set if the PLY reader has to take in account point normals during read. * @param readPointNormal true if the reader has to take in account point normals during read or false otherwise. * @see #isReadPointNormal() */ public void setReadPointNormal(boolean readPointNormal) { this.readPointNormal = readPointNormal; } /** * Get if the read points have to be affected by an identifier. By default * points are identified by an integer identifier that represents the rank of the point within the PLY file.
* The point identification can be deactivated to save memory when dealing * with huge points set. * * @return true if the read points have to be affected by an identifier and false otherwise. * @see #setIdentifyPoints(boolean) */ public boolean isIdentifyPoints() { return identifyPoints; } /** * Set if the read points have to be affected by an identifier. By default * points are identified by an integer identifier that represents the rank of the point within the PLY file.
* The point identification can be deactivated to save memory when dealing * with huge points set. * * @param identifyPoints true if the read points have to be affected by an identifier and false otherwise. * @see #isIdentifyPoints() */ public void setIdentifyPoints(boolean identifyPoints) { this.identifyPoints = identifyPoints; } /** * Check if the reader has to flip Y coordinates of texture. * * @return true if the reader has to flip Y coordinates of * texture and false otherwise. * @see #setFlipTextureY(boolean) */ public boolean isFlipTextureY() { return flipTextureY; } /** * Set if the reader has to flip Y coordinates of texture. * * @param flip true if the reader has to flip Y coordinates of texture and false otherwise. * @see #isFlipTextureY() */ public void setFlipTextureY(boolean flip) { this.flipTextureY = flip; } /** * Read a geometric object from a reader opened onto a PLY file. Due to the * data within the PLY file, returned object can be: *
    *
  • {@link Point3DContainer Point3DContainer} if the PLY file only contains vertex information; *
  • {@link Mesh Mesh} if the PLY file contains vertex and faces information; *
  • {@link IndexedTriangleMesh} if the PLY file contains vertex and faces information and if all faces are triangles; *
* * @param reader the reader to use. * @return the IPolyhedron described by the PLY stream provided by the reader. * @throws IOException if an error occurs. */ @SuppressWarnings("unchecked") public Object read(Reader reader) throws IOException { Object ret = null; T point = null; List> faces = null; Face face = null; IndexedMesh mesh = null; boolean headerFinished = false; boolean vertexElementOk = false; boolean faceElementOk = false; boolean edgeElementOk = false; boolean allElementsRead = false; String line = null; int lineNumber = 0; String[] splittedLine = null; int faceCount = 0; int edgeCount = -1; textures = null; faceCurrentIndex = 0; boolean isTriangleMesh = true; boolean noReadLine = false; long startTime = 0; long endTime = 0; List elementDescriptions = null; PLYElementDescription elementDescription = null; PLYPropertyDescription propertyDescriptor = null; if (reader != null) { startTime = System.currentTimeMillis(); // Instanciate line reader BufferedReader br = null; if (reader instanceof BufferedReader) { br = (BufferedReader) reader; } else { br = new BufferedReader(reader); } dispatchReadStarted(); dispatchReadHeaderStarted(); line = br.readLine(); if (line != null) { lineNumber++; if (line.equalsIgnoreCase("PLY")) { } else { throw new IOException("Cannot read PLY data: at line " + lineNumber + " expected \"ply\" but found " + line); } } else { throw new IOException("Cannot read PLY data: unexpected end of file at line " + lineNumber); } line = br.readLine(); if (line != null) { lineNumber++; if (line.startsWith("format ascii 1.0")) { fileFormat = PLY.FILE_FORMAT_ASCII; } else if (line.startsWith("format binary_little_endian")) { fileFormat = PLY.FILE_FORMAT_BINARY_LE; } else if (line.startsWith("format binary_big_endian")) { fileFormat = PLY.FILE_FORMAT_BINARY_BE; } else { throw new IOException("Cannot read PLY data: unknown format \"" + line + "\" at line " + lineNumber); } } else { throw new IOException("Cannot read PLY data: unexpected end of file at line " + lineNumber); } elementDescriptions = new ArrayList(); // Reading header headerFinished = false; line = br.readLine(); while ((line != null) && (!headerFinished)) { line = line.trim(); lineNumber++; splittedLine = line.split(fieldSeparator); // Read line is not a comment if (!line.startsWith("comment")) { // Read an element description if (splittedLine[0].equals("element")) { if (elementDescription != null) { elementDescriptions.add(elementDescription); dispatchReadElementDescription(elementDescription); } try { elementDescription = new PLYElementDescription(splittedLine[1], Integer.parseInt(splittedLine[2])); if ((FACE_NAME.equalsIgnoreCase(elementDescription.getName())) && (!faceElementOk)) { faceElementOk = true; faceCount = elementDescription.getElementCount(); } else if ((VERTEX_NAME.equalsIgnoreCase(elementDescription.getName())) && (!vertexElementOk)) { vertexElementOk = true; vertexCount = elementDescription.getElementCount(); } else if ((EDGE_NAME.equalsIgnoreCase(elementDescription.getName())) && (!vertexElementOk)) { edgeElementOk = true; edgeCount = elementDescription.getElementCount(); } } catch (NumberFormatException e) { elementDescription = null; throw new IOException("Cannot read PLY data: Invalid element declaration \"" + line + "\" at line " + lineNumber, e); } // Read an element property description } else if (splittedLine[0].equals("property")) { if (elementDescription != null) { if (splittedLine[1].equalsIgnoreCase("list")) { propertyDescriptor = new PLYPropertyListDescription(splittedLine[4], PLY.getType(splittedLine[2]), PLY.getType(splittedLine[3])); elementDescription.addPropertyDescriptor(propertyDescriptor); } else if (splittedLine.length == 3) { propertyDescriptor = new PLYPropertyDescription(PLY.getType(splittedLine[1]), splittedLine[2]); elementDescription.addPropertyDescriptor(propertyDescriptor); } else { throw new IOException("Cannot read PLY data: invalid property declaration \"" + line + "\" at line " + lineNumber); } } else { throw new IOException("Cannot read PLY data: property declared outside element \"" + line + "\" at line " + lineNumber); } } else if (splittedLine[0].equals("end_header")) { if (elementDescription != null) { elementDescriptions.add(elementDescription); dispatchReadElementDescription(elementDescription); elementDescription = null; } if (vertexElementOk && !((faceElementOk && (faceCount > 0)) || (edgeElementOk && (edgeCount > 0)))) { geometryType = GEOM_POINTS; } else if (vertexElementOk && ((faceElementOk && (faceCount > 0)) || (edgeElementOk && (edgeCount > 0)))) { geometryType = GEOM_POLYHEDRON; } headerFinished = true; noReadLine = true; } // Reading comment } else { // Special case if a texture is read if (splittedLine.length >= 3) { if ("TextureFile".equalsIgnoreCase(splittedLine[1])) { String resource = splittedLine[2]; if (textures == null) { textures = new ArrayList(); } textures.add(new TexturePath(PathUtil.getFileName(resource), textures.size(), resource)); } } } if (noReadLine) { noReadLine = false; } else { line = br.readLine(); } } // Post process header (remove empty elements, ...) propertyIndexes = processHeader(elementDescriptions); dispatchReadHeaderFinished(); // Reading data with respect to element order if ((elementDescriptions != null) && (elementDescriptions.size() > 0)) { dispatchReadDataStarted(); if (getFileFormat() == PLY.FILE_FORMAT_ASCII) { line = br.readLine(); Iterator descriptorIter = elementDescriptions.iterator(); int readElementCount = 0; while (descriptorIter.hasNext()) { elementDescription = descriptorIter.next(); readElementCount = 0; dispatchReadElementsStarted(elementDescription); // Reading points if (VERTEX_NAME.equalsIgnoreCase(elementDescription.getName())) { Point3DContainer points = null; if (getPoint3DClass() != null) { if (Point3D.class.isAssignableFrom(getPoint3DClass())) { points = JeometryFactory.createPoint3DContainer(vertexCount); } else if (Point2D.class.isAssignableFrom(getPoint3DClass())) { Jeometry.logger.log(Level.WARNING, "2D point container read from PLY is not yet implemented."); System.err.println("2D point container read from PLY is not yet implemented."); return null; } } else { points = JeometryFactory.createPoint3DContainer(vertexCount); } ret = points; while ((line != null) && (!allElementsRead) && (readElementCount < elementDescription.getElementCount())) { line = line.trim(); lineNumber++; splittedLine = line.split(fieldSeparator); try { point = readPoint3D(splittedLine, propertyIndexes); if (accept(point)) { if (isIdentifyPoints()) { if (point instanceof Identified) { if (((Identified)point).getIdentification() == -1) { ((Identified)point).setIdentification(readElementCount); } } if (point instanceof Named) { ((Named)point).setName(pointName); } } points.add(point); } dispatchReadVertex(point); point = null; } catch (Exception e) { throw new IOException("Cannot read PLY data: invalid vertex element \"" + line + "\" at line " + lineNumber, e); } readElementCount++; if (noReadLine) { noReadLine = false; } else { line = br.readLine(); } } // Reading faces } else if (FACE_NAME.equalsIgnoreCase(elementDescription.getName())) { while ((line != null) && (!allElementsRead) && (readElementCount < elementDescription.getElementCount())) { line = line.trim(); lineNumber++; splittedLine = line.split(fieldSeparator); if (faces == null) { faces = new ArrayList>(); } try { face = readFace(splittedLine, elementDescription); faceCount++; if (face instanceof Triangle) { isTriangleMesh &= true; if (face instanceof Identified) { ((Identified)face).setIdentification(faceCount); } if (face instanceof Named) { ((Named)face).setName(faceTriangleName); } } else { isTriangleMesh &= false; } faces.add(face); dispatchReadFace(face); } catch (Exception e) { throw new IOException("Cannot read PLY data: invalid face element \"" + line + "\" at line " + lineNumber, e); } readElementCount++; if (noReadLine) { noReadLine = false; } else { line = br.readLine(); } splittedLine = null; } } else { while ((line != null) && (!allElementsRead) && (readElementCount < elementDescription.getElementCount())) { line = line.trim(); lineNumber++; readElementCount++; if (noReadLine) { noReadLine = false; } else { line = br.readLine(); } } } // Check if the read elements are consistents with descriptor // declared count if (readElementCount != elementDescription.getElementCount()) { throw new IOException("Cannot read PLY data: inconsistent " + elementDescription.getName() + " count. Should be " + elementDescription.getElementCount() + " but read " + readElementCount + " \"" + line + "\" at line " + lineNumber); } dispatchReadElementsFinished(elementDescription); } } else { } dispatchReadDataFinished(); dispatchReadFinished(); } else { throw new IOException("Cannot read PLY data: no descriptors provided (error within PLY header file) \"" + line + "\" at line " + lineNumber); } } else { throw new IOException("Cannot read PLY data: at line " + lineNumber + " expected \"ply\" but found " + line); } endTime = System.currentTimeMillis(); if (geometryType == GEOM_POINTS) { logger.log(Level.INFO, "PLY points file read in " + (endTime - startTime) / 1000.0f + "s"); return ret; } else if (geometryType == GEOM_POLYHEDRON) { if (isTriangleMesh) { mesh = JeometryFactory.createIndexedTriangleMesh((Point3DContainer)ret); ((IndexedTriangleMesh) mesh).setVerticesSource((Point3DContainer)ret); if (textures != null) { if (mesh instanceof TextureManager) { ((TextureManager) mesh).setTextures(textures); } } } else { mesh = JeometryFactory.createIndexedMesh(); if (mesh != null) { ((IndexedMesh)mesh).setVerticesSource((Point3DContainer)ret); } else { logger.log(Level.SEVERE, "Cannot create indexed mesh from geometry factory."); } } if (faces != null) { for (int i = 0; i < faces.size(); i++) { mesh.addFace(faces.get(i)); } } mesh.validateIndexes(); logger.log(Level.INFO, "PLY file read in " + (endTime - startTime) / 1000.0f + "s"); return mesh; } else { logger.log(Level.INFO, "PLY file read with no result in " + (endTime - startTime) / 1000.0f + "s"); return null; } } /** * Read a geometric object from an input stream opened onto a PLY file. Due to * the data within the PLY file, returned object can be: *
    *
  • {@link Point3DContainer a 3D point container} if the PLY file only contains vertex information;
  • *
  • {@link IndexedMesh an indexed mesh} if the PLY file contains vertex and faces information;
  • *
  • {@link IndexedTriangleMesh an indexed triangle mesh} if the PLY file contains vertex and faces information and * if all faces are triangles;
  • *
* @param is the input stream to use. * @return the IPolyhedron described by the PLY stream provided by the reader. * @throws UnsupportedEncodingException if an error occurs. * @throws IOException if an error occurs. */ public Object read(InputStream is) throws UnsupportedEncodingException, IOException { if (is != null) { Reader r = null; try { is.mark(65536); faceCurrentIndex = 0; PLYFileDescriptor fileDescriptor = readHeader(is); if (fileDescriptor.getFileFormat() == PLY.FILE_FORMAT_ASCII) { is.reset(); r = new InputStreamReader(is, charset); return read(r); } else { return readBinaryData(is, fileDescriptor); } } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException("Error reading ply file : " + e.getMessage(), e); } finally { try { r.close(); } catch (Exception e) { r = null; } } } else { throw new IOException("Cannot read PLY data to null output stream"); } } /** * Read a geometric object from the PLY file pointed by the uri * given in parameter. Due to the data within the PLY file, returned object * can be: *
    *
  • {@link Point3DContainer 3D point container} if the PLY file only contains vertex information; *
  • {@link IndexedMesh indexed mesh} if the PLY file contains vertex and faces information; *
  • {@link IndexedTriangleMesh triangle mesh} if the PLY file contains vertex and faces information and if all faces are triangles; *
* @param uri the URI of the input source. * @return the geometric object described by the PLY stream provided by the reader. * @throws UnsupportedEncodingException if the PLY source provide unsupported encoding. * @throws IOException if an error occurs. */ public Object read(String uri) throws UnsupportedEncodingException, IOException { Object result = null; if (uri != null) { if ("ZIP".equalsIgnoreCase(PathUtil.getExtension(uri))) { ZipFile zipFile = null; Enumeration entries = null; InputStream stream = null; try { zipFile = new ZipFile(new File(uri)); entries = zipFile.entries(); if (entries != null) { stream = zipFile.getInputStream(entries.nextElement()); result = read(stream); try { stream.close(); stream = null; zipFile.close(); zipFile = null; } catch (Exception e) { } return result; } else { zipFile.close(); zipFile = null; throw new IOException("No entries available within ZIP file " + uri); } } catch (Exception e) { try { zipFile.close(); zipFile = null; } catch (IOException e1) { } throw new IOException("Problem during read from ZIP file " + uri, e); } } else { InputStream is = null; is = IOStreamUtil.getBufferedInputStream(uri); resourcePath = PathUtil.URIToPath(uri); return read(is); } } else { logger.log(Level.WARNING, "Cannot read PLY data from " + uri); return null; } } /** * Read a geometric object from a PLY file. Due to the data within the PLY * file, returned object can be: *
    *
  • {@link Point3DContainer 3D point container} if the PLY file only contains vertex information; *
  • {@link IndexedMesh indexed mesh} if the PLY file contains vertex and faces information; *
  • {@link IndexedTriangleMesh triangle mesh} if the PLY file contains vertex and faces information and if all faces are triangles; *
* * @param file the PLY file to read (can be a ZIP file that contains the PLY file itself). * @return the geometric object described by the PLY stream provided by the reader. * @throws UnsupportedEncodingException if the PLY source provide unsupported encoding. * @throws IOException if an error occurs. */ public Object read(File file) throws UnsupportedEncodingException, IOException { Object result = null; if (file != null) { if ("ZIP".equalsIgnoreCase(PathUtil.getExtension(file.getPath()))) { ZipFile zipFile = null; Enumeration entries = null; InputStream stream = null; try { zipFile = new ZipFile(file); entries = zipFile.entries(); if (entries != null) { stream = zipFile.getInputStream(entries.nextElement()); result = read(stream); try { stream.close(); stream = null; zipFile.close(); zipFile = null; } catch (Exception e) { } return result; } else { zipFile.close(); zipFile = null; throw new IOException("No entries available within ZIP file " + file.getPath()); } } catch (Exception e) { try { zipFile.close(); zipFile = null; } catch (IOException e1) { } throw new IOException("Problem during read from ZIP file " + file.getPath() + ":" + e.getMessage(), e); } } else { FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis); try { resourcePath = PathUtil.URIToPath(file.getPath()); Object object = read(bis); resourcePath = null; return object; } catch (UnsupportedEncodingException e) { resourcePath = null; throw new IOException("Problem during read from file " + file.getPath() + ":" + e.getMessage(), e); } catch (IOException e) { resourcePath = null; throw e; } finally { resourcePath = null; try { fis.close(); } catch (IOException e) { fis = null; } } } } else { resourcePath = null; logger.log(Level.WARNING, "Cannot read PLY data from file " + file); throw new IOException("Cannot read PLY data from null file"); } } /** * Attache the given {@link PLYReaderListener listener} to the reader. * @param listener the {@link PLYReaderListener listener} to add to the reader. * @see #removePLYReaderListener(PLYReaderListener) */ public void addPLYReaderListener(PLYReaderListener listener) { if (listener != null) { if (listeners == null) { listeners = new HashSet(); listeners.add(listener); } else { if (!listeners.contains(listener)) { listeners.add(listener); } } } } /** * Read the description of a PLY file (header) from an {@link java.io.InputStream input stream}. * @param stream the {@link java.io.InputStream input stream} that point the PLY file. * @return the description of a PLY file (header). * @throws IOException if an error occurs. */ public PLYFileDescriptor readHeader(InputStream stream) throws IOException { PLYFileDescriptor fileDescriptor = null; if (stream != null) { fileDescriptor = new PLYFileDescriptor(); Collection elementDescriptions = null; PLYElementDescription elementDescription = null; PLYPropertyDescription propertyDescriptor = null; boolean headerFinished = false; String[] splittedLine = null; boolean vertexElementOk = false; boolean faceElementOk = false; boolean edgeElementOk = false; boolean noReadLine = false; boolean vertexXOk = false; boolean vertexYOk = false; boolean vertexZOk = false; textures = null; faceCurrentIndex = 0; int faceCount = 0; int edgeCount = 0; int lineNumber = 0; dispatchReadHeaderStarted(); String line = readLine(stream); if (line != null) { lineNumber++; if (line.equalsIgnoreCase("PLY")) { } else { throw new IOException("Cannot read PLY data: at line " + lineNumber + " expected \"ply\" but found " + line); } } else { throw new IOException("Cannot read PLY data: unexpected end of file at line " + lineNumber); } line = readLine(stream); if (line != null) { lineNumber++; if (line.contains("ascii")) { fileDescriptor.setFileFormat(PLY.FILE_FORMAT_ASCII); } else if (line.contains("binary_little_endian")) { fileDescriptor.setFileFormat(PLY.FILE_FORMAT_BINARY_LE); } else if (line.contains("binary_big_endian")) { fileDescriptor.setFileFormat(PLY.FILE_FORMAT_BINARY_BE); } else { throw new IOException("Cannot read PLY data: unknown format \"" + line + "\" at line " + lineNumber); } } else { throw new IOException("Cannot read PLY data: unexpected end of file at line " + lineNumber); } elementDescriptions = new ArrayList(); // Reading header headerFinished = false; line = readLine(stream); while ((line != null) && (!headerFinished)) { line = line.trim(); lineNumber++; splittedLine = line.split(fieldSeparator); // Read line is not a comment if (!line.startsWith("comment")) { // Read an element description if (splittedLine[0].equals("element")) { if (elementDescription != null) { elementDescriptions.add(elementDescription); dispatchReadElementDescription(elementDescription); } try { elementDescription = new PLYElementDescription(splittedLine[1], Integer.parseInt(splittedLine[2])); if ((FACE_NAME.equalsIgnoreCase(elementDescription.getName())) && (!faceElementOk)) { faceElementOk = true; faceCount = elementDescription.getElementCount(); fileDescriptor.setFaceCount(faceCount); } else if ((VERTEX_NAME.equalsIgnoreCase(elementDescription.getName())) && (!vertexElementOk)) { vertexElementOk = true; vertexCount = elementDescription.getElementCount(); fileDescriptor.setVertexCount(vertexCount); } else if ((EDGE_NAME.equalsIgnoreCase(elementDescription.getName())) && (!vertexElementOk)) { edgeElementOk = true; edgeCount = elementDescription.getElementCount(); fileDescriptor.setEdgeCount(edgeCount); } } catch (NumberFormatException e) { elementDescription = null; throw new IOException("Cannot read PLY data: Invalid element declaration \"" + line + "\" at line " + lineNumber, e); } // Read an element property description } else if (splittedLine[0].equals("property")) { if (elementDescription != null) { if (splittedLine[1].equalsIgnoreCase("list")) { propertyDescriptor = new PLYPropertyListDescription(splittedLine[4], PLY.getType(splittedLine[2]), PLY.getType(splittedLine[3])); elementDescription.addPropertyDescriptor(propertyDescriptor); } else if (splittedLine.length == 3) { propertyDescriptor = new PLYPropertyDescription(PLY.getType(splittedLine[1]), splittedLine[2]); elementDescription.addPropertyDescriptor(propertyDescriptor); if (VERTEX_NAME.equalsIgnoreCase(elementDescription.getName())) { if (plyXPropName.equalsIgnoreCase(propertyDescriptor.getName())) { vertexXOk = true; } else if (plyYPropName.equalsIgnoreCase(propertyDescriptor.getName())) { vertexYOk = true; } else if (plyZPropName.equalsIgnoreCase(propertyDescriptor.getName())) { vertexZOk = true; } } } else { throw new IOException("Cannot read PLY data: invalid property declaration \"" + line + "\" at line " + lineNumber); } } else { throw new IOException("Cannot read PLY data: property declared outside element \"" + line + "\" at line " + lineNumber); } } else if (splittedLine[0].equals("end_header")) { if (elementDescription != null) { elementDescriptions.add(elementDescription); dispatchReadElementDescription(elementDescription); elementDescription = null; } if (vertexElementOk && !((faceElementOk && (faceCount > 0)) || (edgeElementOk && (edgeCount > 0)))) { geometryType = GEOM_POINTS; } else if (vertexElementOk && ((faceElementOk && (faceCount > 0)) || (edgeElementOk && (edgeCount > 0)))) { geometryType = GEOM_POLYHEDRON; } headerFinished = true; noReadLine = true; } // Reading comment } else { // Special case if a texture is read if (splittedLine.length >= 3) { if ("TextureFile".equalsIgnoreCase(splittedLine[1])) { String resource = splittedLine[2]; if (textures == null) { textures = new ArrayList(); } if (!PathUtil.isAbsolutePath(resource) && (resourcePath == null)) { logger.log(Level.WARNING, "Cannot determine absolute location of texture " + resource); } else { String str = PathUtil.URIToPath(PathUtil.getDirectory(resourcePath)); str = PathUtil.URIToPath(str + File.separator + resource); resource = str; } textures.add(new TexturePath(PathUtil.getFileName(resource), textures.size(), resource)); } } } if (noReadLine) { noReadLine = false; } else { line = readLine(stream); } } // Save the vertex type: if ((vertexXOk) && (vertexYOk) && (vertexZOk)) { fileDescriptor.setVertexType(PLY.VERTEX_TYPE_3D); } else if ((vertexXOk) && (vertexYOk)) { fileDescriptor.setVertexType(PLY.VERTEX_TYPE_2D); } fileDescriptor.setHeaderLineCount(lineNumber); fileDescriptor.setElementDescriptors(elementDescriptions); // Post process header (remove empty elements, ...) propertyIndexes = processHeader(elementDescriptions); dispatchReadHeaderFinished(); } return fileDescriptor; } /** * Read binary PLY data. * @param is the input stream to use * @param fileDescriptor the description of the PLY file */ private Object readBinaryData(InputStream is, PLYFileDescriptor fileDescriptor) throws IOException { Point3DContainer points3D = null; Point2DContainer points2D = null; List> faces = null; int faceCurrentIndex = -1; Face face = null; IndexedMesh mesh = null; long startTime = 0; long endTime = 0; boolean allElementsRead = false; boolean isTriangleMesh = true; BufferedInputStream bis = null; dispatchReadDataStarted(); int lastReadBytes = Integer.MAX_VALUE; if (is != null) { if (((fileDescriptor.getFileFormat() == PLY.FILE_FORMAT_BINARY_LE) || (fileDescriptor.getFileFormat() == PLY.FILE_FORMAT_BINARY_BE)) && (fileDescriptor != null)) { if (getPoint3DClass() != null) { if (Point3D.class.isAssignableFrom(getPoint3DClass())) { if (fileDescriptor.getVertexCount() > 0) { points3D = JeometryFactory.createPoint3DContainer(fileDescriptor.getVertexCount()); } else { points3D = JeometryFactory.createPoint3DContainer(); } } else if (Point2D.class.isAssignableFrom(getPoint3DClass())) { if (fileDescriptor.getVertexCount() > 0) { points2D = JeometryFactory.createPoint2DContainer(fileDescriptor.getVertexCount()); } else { points2D = JeometryFactory.createPoint2DContainer(); } } } else { if (fileDescriptor.getVertexType() == PLY.VERTEX_TYPE_3D) { if (fileDescriptor.getVertexCount() > 0) { points3D = JeometryFactory.createPoint3DContainer(fileDescriptor.getVertexCount()); } else { points3D = JeometryFactory.createPoint3DContainer(); } } else if (fileDescriptor.getVertexType() == PLY.VERTEX_TYPE_2D) { if (fileDescriptor.getVertexCount() > 0) { points2D = JeometryFactory.createPoint2DContainer(fileDescriptor.getVertexCount()); } else { points2D = JeometryFactory.createPoint2DContainer(); } } } if (is instanceof BufferedInputStream) { bis = (BufferedInputStream) is; } else { bis = new BufferedInputStream(is); } byte[] bytes = null; boolean eof = false; Collection elementDescriptions = fileDescriptor.getElementDescriptors(); PLYElementDescription elementDescription = null; if (elementDescriptions != null) { Iterator descriptorIter = elementDescriptions.iterator(); int readElementCount = 0; while (descriptorIter.hasNext()) { elementDescription = descriptorIter.next(); readElementCount = 0; dispatchReadElementsStarted(elementDescription); // Reading points if (VERTEX_NAME.equalsIgnoreCase(elementDescription.getName())) { int elementBinarySize = 0; // Compute properties binary size if (elementDescription.getPropertyDescriptors() != null) { Iterator iter = elementDescription.getPropertyDescriptors().iterator(); PLYPropertyDescription description = null; while (iter.hasNext()) { description = iter.next(); if (PLY.getTypeSize(description.getType()) > 0) { elementBinarySize += PLY.getTypeSize(description.getType()); } else { throw new IOException("Unknown PLY type " + description.getType()); } } } bytes = new byte[elementBinarySize]; T point3d = null; Point2D point2d = null; eof = (lastReadBytes <= 0); while ((!eof) && (!allElementsRead) && (readElementCount < elementDescription.getElementCount())) { lastReadBytes = bis.read(bytes); if (fileDescriptor.getVertexType() == PLY.VERTEX_TYPE_3D) { point3d = readPoint3D(bytes, elementDescription, fileDescriptor); if (accept(point3d)) { if (isIdentifyPoints()) { if (point3d instanceof Identified) { ((Identified)point3d).setIdentification(readElementCount); } if (point3d instanceof Named) { ((Named)point3d).setName(pointName); } } points3D.add(point3d); } else if ((fileDescriptor.getVertexType() == PLY.VERTEX_TYPE_2D)) { point2d = readPoint2D(bytes, elementDescription, fileDescriptor); if (accept(point2d)) { if (isIdentifyPoints()) { if (point2d instanceof Identified) { ((Identified)point2d).setIdentification(readElementCount); } if (point2d instanceof Named) { ((Named)point2d).setName(pointName); } } } } } dispatchReadVertex(point3d); point3d = null; point2d = null; readElementCount++; } // Reading faces } else if (FACE_NAME.equalsIgnoreCase(elementDescription.getName())) { // Get the list property count field binary size if ((elementDescription.getPropertyDescriptors() != null) && (elementDescription.getPropertyDescriptors().size() > 0)) { PLYPropertyDescription description = elementDescription.getPropertyDescriptors().get(0); if (description instanceof PLYPropertyListDescription) { readElementCount = 0; faces = new ArrayList>(elementDescription.getElementCount()); eof = (lastReadBytes < 0); while ((!eof) && (!allElementsRead) && (readElementCount < elementDescription.getElementCount())) { face = readFace(bis, elementDescription, fileDescriptor); if (face != null) { faceCurrentIndex = faceCurrentIndex + 1; faces.set(faceCurrentIndex, face); } else { logger.log(Level.INFO, "Cannot read face."); } dispatchReadFace(face); readElementCount++; } } else { throw new IOException("Unknown PLY type " + description.getType() + " as a list type is expected for faces description"); } } } else { } // Check if the read elements are consistents with descriptor // declared count if (readElementCount != elementDescription.getElementCount()) { throw new IOException("Cannot read PLY data: inconsistent " + elementDescription.getName() + " count. Should be " + elementDescription.getElementCount() + " but read " + readElementCount); } dispatchReadElementsFinished(elementDescription); } } } else { throw new IOException("Cannot read PLY binary data from non binary file."); } } else { throw new IOException("Cannot read PLY binary data from null input stream."); } if (geometryType == GEOM_POINTS) { if (fileDescriptor.getVertexType() == PLY.VERTEX_TYPE_3D) { logger.log(Level.FINE, "PLY 3D points file read in " + (endTime - startTime) / 1000.0f + "s"); dispatchReadDataFinished(); return points3D; } else if (fileDescriptor.getVertexType() == PLY.VERTEX_TYPE_2D) { logger.log(Level.FINE, "PLY 2D points file read in " + (endTime - startTime) / 1000.0f + "s"); dispatchReadDataFinished(); return points2D; } else { logger.log(Level.FINE, "PLY unknown points file read in " + (endTime - startTime) / 1000.0f + "s"); dispatchReadDataFinished(); return null; } } else if (geometryType == GEOM_POLYHEDRON) { if (fileDescriptor.getVertexType() == PLY.VERTEX_TYPE_3D) { if (isTriangleMesh) { mesh = JeometryFactory.createIndexedTriangleMesh(points3D); if (textures != null) { if (mesh instanceof TextureManager) { ((TextureManager) mesh).setTextures(textures); } else { logger.log(Level.WARNING, "PLY file contains "+textures.size()+" but output mesh implementation does not support it."); } } } else { mesh = JeometryFactory.createIndexedMesh(); if (mesh != null) { mesh.setVerticesSource(points3D); } } if (faces != null) { for (int i = 0; i < faces.size(); i++) { mesh.addFace(faces.get(i)); } faces = null; } mesh.validateIndexes(); logger.log(Level.INFO, "PLY file read in " + (endTime - startTime) / 1000.0f + "s"); dispatchReadDataFinished(); return mesh; } else { dispatchReadDataFinished(); throw new IOException("PLY reader cannot handle 2D polygon."); } } else { logger.log(Level.INFO, "PLY file read with no result in " + (endTime - startTime) / 1000.0f + "s"); dispatchReadDataFinished(); return null; } } /** * Detach the the given {@link PLYReaderListener listener} from the reader. * @param listener the {@link PLYReaderListener listener} to detach from the reader. * @see #addPLYReaderListener(PLYReaderListener) */ public void removePLYReaderListener(PLYReaderListener listener) { if ((listener != null) && (listeners != null)) { listeners.remove(listener); } } /** * Check if the file located by the given path is a valid PLY file. * * @param uri the path of the file to check. * @return true if the file is a valid PLY and false otherwise. * @throws IOException if an I/O error occurs. * @see #checkValidity(Reader) * @see #checkValidity(InputStream) */ public boolean checkValidity(String uri) throws IOException { boolean ok = false; if (uri != null) { BufferedInputStream bis = null; bis = IOStreamUtil.getBufferedInputStream(uri); ok = checkValidity(bis); } else { logger.log(Level.WARNING, "Cannot read PLY data from " + uri); } return ok; } /** * Check if the given {@link java.io.InputStream input stream} is a valid PLY * input stream. * * @param stream the {@link java.io.InputStream input stream} to check. * @return true if the {@link java.io.InputStream input stream} is a valid PLY input stream and false otherwise. * @throws IOException if an I/O error occurs. * @see #checkValidity(Reader) * @see #checkValidity(String) */ public boolean checkValidity(InputStream stream) throws IOException { boolean ok = false; if (stream != null) { Reader r = null; try { r = new InputStreamReader(stream, charset); return checkValidity(r); } catch (Exception e) { throw new IOException("Error reading file : " + e.getMessage(), e); } finally { try { r.close(); } catch (Exception e) { r = null; } } } return ok; } /** * Check if the given {@link java.io.Reader reader} is a valid PLY reader. * @param reader the {@link java.io.Reader reader} to check. * @return true if the {@link java.io.Reader reader} is a valid PLY reader and false otherwise. * @throws IOException if an I/O error occurs. * @see #checkValidity(InputStream) * @see #checkValidity(String) */ public boolean checkValidity(Reader reader) throws IOException { boolean ok = false; if (reader != null) { BufferedReader br = null; if (reader instanceof BufferedReader) { br = (BufferedReader) reader; } else { // Instanciate line reader br = new BufferedReader(reader); } String line = br.readLine(); if ("PLY".equalsIgnoreCase(line)) { ok = true; } } return ok; } protected void dispatchReadStarted() { if (listeners != null) { Iterator iter = listeners.iterator(); while (iter.hasNext()) { iter.next().readStarted(); } } } protected void dispatchReadFinished() { if (listeners != null) { Iterator iter = listeners.iterator(); while (iter.hasNext()) { iter.next().readFinished(); } } } protected void dispatchReadHeaderStarted() { if (listeners != null) { Iterator iter = listeners.iterator(); while (iter.hasNext()) { iter.next().readHeaderStarted(); } } } protected void dispatchReadHeaderFinished() { if (listeners != null) { Iterator iter = listeners.iterator(); while (iter.hasNext()) { iter.next().readHeaderFinished(); } } } protected void dispatchReadElementDescription(PLYElementDescription description) { if (listeners != null) { Iterator iter = listeners.iterator(); while (iter.hasNext()) { iter.next().readElementDescription(description); } } } protected void dispatchReadDataStarted() { if (listeners != null) { Iterator iter = listeners.iterator(); while (iter.hasNext()) { iter.next().readDataStarted(); } } } protected void dispatchReadDataFinished() { if (listeners != null) { Iterator iter = listeners.iterator(); while (iter.hasNext()) { iter.next().readDataFinished(); } } } protected void dispatchReadElementsStarted(PLYElementDescription description) { if (listeners != null) { Iterator iter = listeners.iterator(); while (iter.hasNext()) { iter.next().readElementsStarted(description); } } } protected void dispatchReadElementsFinished(PLYElementDescription description) { if (listeners != null) { Iterator iter = listeners.iterator(); while (iter.hasNext()) { iter.next().readElementsFinished(description); } } } protected void dispatchReadVertex(Point3D vertex) { if (listeners != null) { Iterator iter = listeners.iterator(); while (iter.hasNext()) { iter.next().readVertex(vertex); } } } protected void dispatchReadFace(Face face) { if (listeners != null){ Iterator iter = listeners.iterator(); while(iter.hasNext()){ iter.next().readFace(face); } } } private int[] processHeader(Collection descriptors) { int[] propertiesIndex = null; if (descriptors != null) { propertiesIndex = new int[16]; Iterator iter = descriptors.iterator(); PLYElementDescription descriptor = null; while (iter.hasNext()) { descriptor = iter.next(); if (VERTEX_NAME.equalsIgnoreCase(descriptor.getName())) { propertiesIndex[X_INDEX] = descriptor.getPropertyIndex(plyXPropName); propertiesIndex[Y_INDEX] = descriptor.getPropertyIndex(plyYPropName); propertiesIndex[Z_INDEX] = descriptor.getPropertyIndex(plyZPropName); propertiesIndex[R_INDEX] = descriptor.getPropertyIndexContains(plyRPropName); propertiesIndex[G_INDEX] = descriptor.getPropertyIndexContains(plyGPropName); propertiesIndex[B_INDEX] = descriptor.getPropertyIndexContains(plyBPropName); propertiesIndex[A_INDEX] = descriptor.getPropertyIndexContains(plyAPropName); propertiesIndex[NX_INDEX] = descriptor.getPropertyIndex(plyNxPropName); propertiesIndex[NY_INDEX] = descriptor.getPropertyIndex(plyNyPropName); propertiesIndex[NZ_INDEX] = descriptor.getPropertyIndex(plyNzPropName); propertiesIndex[TEX_U_INDEX] = descriptor.getPropertyIndex(plyVertexTexCoordUPropName); propertiesIndex[TEX_V_INDEX] = descriptor.getPropertyIndex(plyVertexTexCoordVPropName); } else if (FACE_NAME.equals(descriptor.getName())) { propertiesIndex[VERTEX_LIST_INDEX] = descriptor.getPropertyIndex(plyVertexListPropName); propertiesIndex[TEX_INDEX] = descriptor.getPropertyIndex(plyTexIndexPropName); propertiesIndex[TEX_COORDS_INDEX] = descriptor.getPropertyIndex(plyTexCoordPropName); } if (descriptor.getElementCount() < 1) { iter.remove(); } } } return propertiesIndex; } @SuppressWarnings("unchecked") private T readPoint3D(String[] values, int[] propertyIndexes) throws IOException { T pt = null; if (getPoint3DClass() != null) { try { pt = (T)getPoint3DClass().getDeclaredConstructor().newInstance(); if (pt instanceof Point3D) { ((Point3D) pt).setX(Double.parseDouble(values[propertyIndexes[X_INDEX]])); ((Point3D) pt).setY(Double.parseDouble(values[propertyIndexes[Y_INDEX]])); ((Point3D) pt).setZ(Double.parseDouble(values[propertyIndexes[Z_INDEX]])); } else if (pt instanceof Point2D) { ((Point2D) pt).setX(Double.parseDouble(values[propertyIndexes[X_INDEX]])); ((Point2D) pt).setY(Double.parseDouble(values[propertyIndexes[Y_INDEX]])); } else { Jeometry.logger.log(Level.WARNING, "Instanciated point class is not a 3D/2D point."); return null; } } catch (InstantiationException e) { throw new IOException("Declared point class " + getPoint3DClass().getSimpleName() + " cannot be instanciated", e); } catch (IllegalAccessException e) { throw new IOException("Declared point class " + getPoint3DClass().getSimpleName() + " cannot be instanciated", e); } catch (Exception e) { throw new IOException("Declared point class " + getPoint3DClass().getSimpleName() + " cannot be instanciated", e); } } else if ((propertyIndexes[X_INDEX] != -1) && (propertyIndexes[Y_INDEX] != -1) && (propertyIndexes[Z_INDEX] != -1)) { try { pt = (T)JeometryFactory.createPoint3D(Double.parseDouble(values[propertyIndexes[X_INDEX]]), Double.parseDouble(values[propertyIndexes[Y_INDEX]]), Double.parseDouble(values[propertyIndexes[Z_INDEX]])); } catch (Exception e) { throw new IOException("Cannot instanciate 3D point: " + e.getMessage(), e); } } else { try { pt = (T)JeometryFactory.createPoint3D(Double.parseDouble(values[propertyIndexes[X_INDEX]]), Double.parseDouble(values[propertyIndexes[Y_INDEX]]), Double.parseDouble(values[propertyIndexes[Z_INDEX]])); } catch (Exception e) { throw new IOException("Cannot instanciate 3D point from GeometryFactory: " + e.getMessage(), e); } } if (pt != null) { // Set point normal if (isReadPointNormal() && (propertyIndexes[NX_INDEX] != -1) && (propertyIndexes[NY_INDEX] != -1) & (propertyIndexes[NZ_INDEX] != -1)) { if (pt instanceof HasNormal) { ((HasNormal)pt).setNormal(JeometryFactory.createPoint3D(Double.parseDouble(values[propertyIndexes[NX_INDEX]]), Double.parseDouble(values[propertyIndexes[NY_INDEX]]), Double.parseDouble(values[propertyIndexes[NZ_INDEX]]))); } else { Jeometry.logger.log(Level.WARNING, "Ignoring point normal as class "+pt.getClass().getSimpleName()+" does not implements HasNormal"); } } // Set Pt Color if ((pt instanceof Colored) && isReadPointColor() && (propertyIndexes[R_INDEX] != -1) && (propertyIndexes[G_INDEX] != -1) && (propertyIndexes[B_INDEX] != -1)) { try { int r = Integer.parseInt(values[propertyIndexes[R_INDEX]]); int g = Integer.parseInt(values[propertyIndexes[G_INDEX]]); int b = Integer.parseInt(values[propertyIndexes[B_INDEX]]); Color c = new Color(r, g, b); ((Colored) pt).setColor(c); } catch (Exception e) { throw new IOException("Cannot set 3D point color: " + e.getMessage(), e); } } } return pt; } @SuppressWarnings("unchecked") private T readPoint3D(byte[] bytes, PLYElementDescription description, PLYFileDescriptor fileDescriptor) throws IOException { T pt = null; int offset = 0; int rgbR = -1; int rgbG = -1; int rgbB = -1; int rgbA = -1; Point3D normal = null; if (getPoint3DClass() != null) { try { pt = (T)getPoint3DClass().getDeclaredConstructor().newInstance(); } catch (InstantiationException e) { throw new IOException("Declared point class " + getPoint3DClass().getSimpleName() + " cannot be instanciated", e); } catch (IllegalAccessException e) { throw new IOException("Declared point class " + getPoint3DClass().getSimpleName() + " cannot be instanciated", e); } catch (Exception e) { throw new IOException("Declared point class " + getPoint3DClass().getSimpleName() + " cannot be instanciated", e); } } else { if (fileDescriptor.getVertexType() == PLY.VERTEX_TYPE_3D) { pt = (T)JeometryFactory.createPoint3D(); } else if (fileDescriptor.getVertexType() == PLY.VERTEX_TYPE_2D) { Jeometry.logger.log(Level.WARNING, "2D point support is not yet implemented."); return null; } else { throw new IOException("Cannot determine vertex type. Vertex type can be either 2D or 3D"); } } Iterator iter = description.getPropertyDescriptors().iterator(); PLYPropertyDescription property = null; byte[] tmp = null; ByteBuffer buffer = ByteBuffer.wrap(bytes); Object value = null; while (iter.hasNext()) { property = iter.next(); if (PLY.TYPE_CHAR == property.getType()) { tmp = new byte[PLY.TYPE_CHAR_SIZE]; buffer.get(tmp, offset, PLY.TYPE_CHAR_SIZE); value = readChar(tmp); } else if (PLY.TYPE_UCHAR == property.getType()) { tmp = new byte[PLY.TYPE_UCHAR_SIZE]; buffer.get(tmp, offset, PLY.TYPE_UCHAR_SIZE); value = readUChar(tmp); } else if (PLY.TYPE_SHORT == property.getType()) { tmp = new byte[PLY.TYPE_SHORT_SIZE]; buffer.get(tmp, offset, PLY.TYPE_SHORT_SIZE); value = readShort(tmp, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_USHORT == property.getType()) { tmp = new byte[PLY.TYPE_USHORT_SIZE]; buffer.get(tmp, offset, PLY.TYPE_USHORT_SIZE); value = readShort(tmp, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_INT == property.getType()) { tmp = new byte[PLY.TYPE_INT_SIZE]; buffer.get(tmp, offset, PLY.TYPE_INT_SIZE); value = readInt(tmp, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_UINT == property.getType()) { tmp = new byte[PLY.TYPE_UINT_SIZE]; buffer.get(tmp, offset, PLY.TYPE_UINT_SIZE); value = readInt(tmp, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_FLOAT == property.getType()) { tmp = new byte[PLY.TYPE_FLOAT_SIZE]; buffer.get(tmp, offset, PLY.TYPE_FLOAT_SIZE); value = readFloat(tmp, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_DOUBLE == property.getType()) { tmp = new byte[PLY.TYPE_DOUBLE_SIZE]; buffer.get(tmp, offset, PLY.TYPE_DOUBLE_SIZE); value = readDouble(tmp, fileDescriptor.getFileFormat()); } else { if (pt instanceof HandleUserProperties) { ((HandleUserProperties)pt).setUserProperty("PLY_" + property.getName(), value); } } if (plyXPropName.equalsIgnoreCase(property.getName())) { if (value instanceof Number) { pt.setX(((Number) value).doubleValue()); } else { Jeometry.logger.log(Level.WARNING, "Ignoring unknown numerical type " + value.getClass() + " for " + plyXPropName + " component"); } } else if (plyYPropName.equalsIgnoreCase(property.getName())) { if (value instanceof Number) { pt.setY(((Number) value).doubleValue()); } else { Jeometry.logger.log(Level.WARNING, "Ignoring unknown numerical type " + value.getClass() + " for " + plyYPropName + " component"); } } else if (plyZPropName.equalsIgnoreCase(property.getName()) && (pt instanceof Point3D)) { if (value instanceof Number) { pt.setZ(((Number) value).doubleValue()); } else { Jeometry.logger.log(Level.WARNING, "Ignoring unknown numerical type " + value.getClass() + " for " + plyZPropName + " component"); } } else if (plyRPropName.equalsIgnoreCase(property.getName())) { rgbR = (int) value; } else if (plyGPropName.equalsIgnoreCase(property.getName())) { rgbG = (int) value; } else if (plyBPropName.equalsIgnoreCase(property.getName())) { rgbB = (int) value; } else if (plyAPropName.equalsIgnoreCase(property.getName())) { rgbA = (int) value; } else if (plyNxPropName.equalsIgnoreCase(property.getName())) { if (normal == null) { normal = JeometryFactory.createPoint3D(); } if (value instanceof Number) { normal.setX(((Number) value).doubleValue()); } else { logger.log(Level.WARNING, "Ignoring unknown numerical type " + value.getClass() + " for " + plyNxPropName + " component"); } } else if (plyNyPropName.equalsIgnoreCase(property.getName())) { if (normal == null) { normal = JeometryFactory.createPoint3D(); } if (value instanceof Number) { normal.setY(((Number) value).doubleValue()); } else { logger.log(Level.WARNING, "Ignoring unknown numerical type " + value.getClass() + " for " + plyNyPropName + " component"); } } else if (plyNzPropName.equalsIgnoreCase(property.getName())) { if (normal == null) { normal = JeometryFactory.createPoint3D(); } if (value instanceof Number) { normal.setZ(((Number) value).doubleValue()); } else { logger.log(Level.WARNING, "Ignoring unknown numerical type " + value.getClass() + " for " + plyNzPropName + " component"); } } else if (plyIDPropName.equalsIgnoreCase(property.getName())) { if (pt instanceof Identified) { ((Identified)pt).setIdentification((int) value); } else if (pt instanceof HandleUserProperties) { ((HandleUserProperties)pt).setUserProperty("PLY_" + property.getName(), value); } } else if (plyVertexTexCoordUPropName.equalsIgnoreCase(property.getName())) { if (pt instanceof Identified) { ((Identified)pt).setIdentification((int) value); } else if (pt instanceof HandleUserProperties) { ((HandleUserProperties)pt).setUserProperty("PLY_" + property.getName(), value); } } else if (plyVertexTexCoordVPropName.equalsIgnoreCase(property.getName())) { if (pt instanceof Identified) { ((Identified)pt).setIdentification((int) value); } else if (pt instanceof HandleUserProperties) { ((HandleUserProperties)pt).setUserProperty("PLY_" + property.getName(), value); } } else { if (pt instanceof HandleUserProperties) { ((HandleUserProperties)pt).setUserProperty("PLY_" + property.getName(), value); } } } if (pt instanceof Colored) { if ((rgbR != -1) && (rgbG != -1) && (rgbB != -1)) { if (rgbA != -1) { ((Colored) pt).setColor(new Color(rgbR, rgbG, rgbB, rgbA)); } else { ((Colored) pt).setColor(new Color(rgbR, rgbG, rgbB)); } } } if (normal != null) { if (pt instanceof HasNormal) { ((HasNormal) pt).setNormal(normal); } } return pt; } private Point2D readPoint2D(byte[] bytes, PLYElementDescription description, PLYFileDescriptor fileDescriptor) throws IOException { Point2D pt = null; int offset = 0; int rgbR = -1; int rgbG = -1; int rgbB = -1; int rgbA = -1; Point3D normal = null; if (getPoint2DClass() != null) { try { pt = getPoint2DClass().getDeclaredConstructor().newInstance(); } catch (InstantiationException e) { throw new IOException("Declared point class " + getPoint2DClass().getSimpleName() + " cannot be instanciated", e); } catch (IllegalAccessException e) { throw new IOException("Declared point class " + getPoint2DClass().getSimpleName() + " cannot be instanciated", e); } catch (Exception e) { throw new IOException("Declared point class " + getPoint2DClass().getSimpleName() + " cannot be instanciated", e); } } else { Jeometry.logger.log(Level.WARNING, "2D point support is not yet implemented."); return null; } Iterator iter = description.getPropertyDescriptors().iterator(); PLYPropertyDescription property = null; byte[] tmp = null; ByteBuffer buffer = ByteBuffer.wrap(bytes); Object value = null; while (iter.hasNext()) { property = iter.next(); if (PLY.TYPE_CHAR == property.getType()) { tmp = new byte[PLY.TYPE_CHAR_SIZE]; buffer.get(tmp, offset, PLY.TYPE_CHAR_SIZE); value = readChar(tmp); } else if (PLY.TYPE_UCHAR == property.getType()) { tmp = new byte[PLY.TYPE_UCHAR_SIZE]; buffer.get(tmp, offset, PLY.TYPE_UCHAR_SIZE); value = readUChar(tmp); } else if (PLY.TYPE_SHORT == property.getType()) { tmp = new byte[PLY.TYPE_SHORT_SIZE]; buffer.get(tmp, offset, PLY.TYPE_SHORT_SIZE); value = readShort(tmp, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_USHORT == property.getType()) { tmp = new byte[PLY.TYPE_USHORT_SIZE]; buffer.get(tmp, offset, PLY.TYPE_USHORT_SIZE); value = readShort(tmp, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_INT == property.getType()) { tmp = new byte[PLY.TYPE_INT_SIZE]; buffer.get(tmp, offset, PLY.TYPE_INT_SIZE); value = readInt(tmp, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_UINT == property.getType()) { tmp = new byte[PLY.TYPE_UINT_SIZE]; buffer.get(tmp, offset, PLY.TYPE_UINT_SIZE); value = readInt(tmp, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_FLOAT == property.getType()) { tmp = new byte[PLY.TYPE_FLOAT_SIZE]; buffer.get(tmp, offset, PLY.TYPE_FLOAT_SIZE); value = readFloat(tmp, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_DOUBLE == property.getType()) { tmp = new byte[PLY.TYPE_DOUBLE_SIZE]; buffer.get(tmp, offset, PLY.TYPE_DOUBLE_SIZE); value = readDouble(tmp, fileDescriptor.getFileFormat()); } else { if (pt instanceof HandleUserProperties) { ((HandleUserProperties)pt).setUserProperty("PLY_" + property.getName(), value); } } if (plyXPropName.equalsIgnoreCase(property.getName())) { if (value instanceof Number) { pt.setX(((Number) value).doubleValue()); } else { Jeometry.logger.log(Level.WARNING, "Ignoring unknown numerical type " + value.getClass() + " for " + plyXPropName + " component"); } } else if (plyYPropName.equalsIgnoreCase(property.getName())) { if (value instanceof Number) { pt.setY(((Number) value).doubleValue()); } else { Jeometry.logger.log(Level.WARNING, "Ignoring unknown numerical type " + value.getClass() + " for " + plyYPropName + " component"); } } else if (plyRPropName.equalsIgnoreCase(property.getName())) { rgbR = (int) value; } else if (plyGPropName.equalsIgnoreCase(property.getName())) { rgbG = (int) value; } else if (plyBPropName.equalsIgnoreCase(property.getName())) { rgbB = (int) value; } else if (plyAPropName.equalsIgnoreCase(property.getName())) { rgbA = (int) value; } else if (plyNxPropName.equalsIgnoreCase(property.getName())) { if (normal == null) { normal = JeometryFactory.createPoint3D(); } if (value instanceof Number) { normal.setX(((Number) value).doubleValue()); } else { logger.log(Level.WARNING, "Ignoring unknown numerical type " + value.getClass() + " for " + plyNxPropName + " component"); } } else if (plyNyPropName.equalsIgnoreCase(property.getName())) { if (normal == null) { normal = JeometryFactory.createPoint3D(); } if (value instanceof Number) { normal.setY(((Number) value).doubleValue()); } else { logger.log(Level.WARNING, "Ignoring unknown numerical type " + value.getClass() + " for " + plyNyPropName + " component"); } } else if (plyNzPropName.equalsIgnoreCase(property.getName())) { if (normal == null) { normal = JeometryFactory.createPoint3D(); } if (value instanceof Number) { normal.setZ(((Number) value).doubleValue()); } else { logger.log(Level.WARNING, "Ignoring unknown numerical type " + value.getClass() + " for " + plyNzPropName + " component"); } } else if (plyIDPropName.equalsIgnoreCase(property.getName())) { if (pt instanceof Identified) { ((Identified)pt).setIdentification((int) value); } else if (pt instanceof HandleUserProperties) { ((HandleUserProperties)pt).setUserProperty("PLY_" + property.getName(), value); } } else if (plyVertexTexCoordUPropName.equalsIgnoreCase(property.getName())) { if (pt instanceof Identified) { ((Identified)pt).setIdentification((int) value); } else if (pt instanceof HandleUserProperties) { ((HandleUserProperties)pt).setUserProperty("PLY_" + property.getName(), value); } } else if (plyVertexTexCoordVPropName.equalsIgnoreCase(property.getName())) { if (pt instanceof Identified) { ((Identified)pt).setIdentification((int) value); } else if (pt instanceof HandleUserProperties) { ((HandleUserProperties)pt).setUserProperty("PLY_" + property.getName(), value); } } else { if (pt instanceof HandleUserProperties) { ((HandleUserProperties)pt).setUserProperty("PLY_" + property.getName(), value); } } } if (pt instanceof Colored) { if ((rgbR != -1) && (rgbG != -1) && (rgbB != -1)) { if (rgbA != -1) { ((Colored) pt).setColor(new Color(rgbR, rgbG, rgbB, rgbA)); } else { ((Colored) pt).setColor(new Color(rgbR, rgbG, rgbB)); } } } if (normal != null) { if (pt instanceof HasNormal) { ((HasNormal) pt).setNormal(normal); } } return pt; } private Face readFace(String[] values, PLYElementDescription description) throws IOException { Face face = null; // a face is at least a triangle (3 vertexes) plus a vertex count field if (values != null) { int[] indexes = null; int textureIndex = -1; Point2DContainer textCoords = null; int currentValueIndex = 0; Iterator propIter = description.getPropertyDescriptors().iterator(); PLYPropertyDescription property = null; while (propIter.hasNext()) { property = propIter.next(); // Reading vertex list property if ((plyVertexListPropName.equalsIgnoreCase(property.getName())) || (plyVertexListPropNameAlt.equalsIgnoreCase(property.getName()))) { try { indexes = new int[Integer.parseInt(values[currentValueIndex])]; currentValueIndex++; for (int i = 1; i <= indexes.length; i++) { indexes[i - 1] = Integer.parseInt(values[i]); currentValueIndex++; } } catch (Exception e) { throw new IOException("Cannot instanciate face: " + e.getMessage(), e); } // Reading a texture coordinates } else if (plyTexCoordPropName.equalsIgnoreCase(property.getName())) { int textCoordCount = Integer.parseInt(values[currentValueIndex]); textCoords = JeometryFactory.createPoint2DContainer(textCoordCount); currentValueIndex++; try { for (int i = 0; i < textCoordCount; i += 2) { textCoords.add(JeometryFactory.createPoint2D(Double.parseDouble(values[currentValueIndex]), Double.parseDouble(values[currentValueIndex + 1]))); currentValueIndex += 2; } } catch (Exception e) { throw new IOException("Cannot instanciate face texture coordinates: " + e.getMessage(), e); } // Reading a texture index } else if (plyTexIndexPropName.equalsIgnoreCase(property.getName())) { try { textureIndex = Integer.parseInt(values[currentValueIndex]); currentValueIndex++; } catch (NumberFormatException e) { throw new IOException("Cannot read face texture index: " + e.getMessage(), e); } } } if (indexes != null) { if (indexes.length == 3) { face = JeometryFactory.createIndexedTriangle(indexes, null); } else { if ((textureIndex > -1) || (textCoords != null)) { face = JeometryFactory.createTexturedIndexedMeshFace(indexes); } else { face = JeometryFactory.createIndexedMeshFace(indexes); } } } if ((face != null) && (face instanceof Texturable)) { if (textCoords != null) { ((Texturable) face).setTextureCoodinates(textCoords); } if ((textureIndex > -1) && (textures != null) && (textureIndex < textures.size())) { ((Texturable) face).setTexture(textures.get(textureIndex)); } } } else { throw new IOException("Cannot instanciate face: not enough indexes"); } return face; } private Face readFace(InputStream is, PLYElementDescription description, PLYFileDescriptor fileDescriptor) throws IOException { Face face = null; if (is != null) { if (description != null) { if (description.getPropertyDescriptors() != null) { byte[] bytes = null; int lastReadBytes = 0; boolean eof = false; int faceVerticesCount = 0; int offset = 0; byte[] tmp = null; int[] facesIndices = null; int textureCoordinatesCount = 0; int textureIndex = 0; Point2DContainer textureCoordinates = null; Iterator propertyIterator = description.getPropertyDescriptors().iterator(); PLYPropertyDescription property = null; while (propertyIterator.hasNext()) { property = propertyIterator.next(); // Reading face vertex indexes if (plyVertexListPropName.equals(property.getName()) || (plyVertexListPropNameAlt.equalsIgnoreCase(property.getName()))) { // Read vertex count for the given face if (PLY.TYPE_CHAR == ((PLYPropertyListDescription) property).getCountType()) { bytes = new byte[PLY.getTypeSize(PLY.TYPE_CHAR)]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (!eof) { faceVerticesCount = readChar(bytes); } } else if (PLY.TYPE_UCHAR == ((PLYPropertyListDescription) property).getCountType()) { bytes = new byte[PLY.getTypeSize(PLY.TYPE_UCHAR)]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (!eof) { faceVerticesCount = readUChar(bytes); } } else if (PLY.TYPE_SHORT == ((PLYPropertyListDescription) property).getCountType()) { bytes = new byte[PLY.getTypeSize(PLY.TYPE_SHORT)]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (!eof) { faceVerticesCount = readShort(bytes, fileDescriptor.getFileFormat()); } } else if (PLY.TYPE_USHORT == ((PLYPropertyListDescription) property).getCountType()) { bytes = new byte[PLY.getTypeSize(PLY.TYPE_USHORT)]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (!eof) { faceVerticesCount = readShort(bytes, fileDescriptor.getFileFormat()); } } else if (PLY.TYPE_INT == ((PLYPropertyListDescription) property).getCountType()) { bytes = new byte[PLY.getTypeSize(PLY.TYPE_INT)]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (!eof) { faceVerticesCount = readInt(bytes, fileDescriptor.getFileFormat()); } } else if (PLY.TYPE_UINT == ((PLYPropertyListDescription) property).getCountType()) { bytes = new byte[PLY.getTypeSize(PLY.TYPE_UINT)]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (!eof) { faceVerticesCount = readInt(bytes, fileDescriptor.getFileFormat()); } } else { throw new IOException( "Unknown PLY count type " + ((PLYPropertyListDescription) property).getCountType() + " for face verteices list description"); } // Read face indices as bytes (this is not include the face vertex // count). // A buffer is created to aggregate all face related bytes. bytes = new byte[getBinaryTypeSize(((PLYPropertyListDescription) property).getValueType()) * faceVerticesCount]; lastReadBytes = is.read(bytes); ByteBuffer buffer = ByteBuffer.wrap(bytes); eof = (lastReadBytes < 0); if (!eof) { int indexTypeSize = getBinaryTypeSize(((PLYPropertyListDescription) property).getValueType()); facesIndices = new int[faceVerticesCount]; try { if (PLY.TYPE_CHAR == ((PLYPropertyListDescription) property).getValueType()) { for (int i = 0; i < facesIndices.length; i++) { tmp = new byte[indexTypeSize]; buffer.get(tmp, offset, indexTypeSize); facesIndices[i] = readChar(tmp); } } else if (PLY.TYPE_DOUBLE == ((PLYPropertyListDescription) property).getValueType()) { logger.log(Level.WARNING, "Invalid face index type " + ((PLYPropertyListDescription) property).getValueType() + " for faces description " + property.getName()); throw new IOException("Invalid face index type " + ((PLYPropertyListDescription) property).getValueType() + " for faces description " + property.getName()); } else if (PLY.TYPE_FLOAT == ((PLYPropertyListDescription) property).getValueType()) { logger.log(Level.WARNING, "Invalid face index type " + ((PLYPropertyListDescription) property).getValueType() + " for faces description " + property.getName()); throw new IOException("Invalid face index type " + ((PLYPropertyListDescription) property).getValueType() + " for faces description " + property.getName()); } else if (PLY.TYPE_INT == ((PLYPropertyListDescription) property).getValueType()) { for (int i = 0; i < facesIndices.length; i++) { tmp = new byte[indexTypeSize]; buffer.get(tmp, offset, indexTypeSize); facesIndices[i] = readInt(tmp, fileDescriptor.getFileFormat()); } } else if (PLY.TYPE_SHORT == ((PLYPropertyListDescription) property).getValueType()) { for (int i = 0; i < facesIndices.length; i++) { tmp = new byte[indexTypeSize]; buffer.get(tmp, offset, indexTypeSize); facesIndices[i] = readShort(tmp, fileDescriptor.getFileFormat()); } } else if (PLY.TYPE_UCHAR == ((PLYPropertyListDescription) property).getValueType()) { for (int i = 0; i < facesIndices.length; i++) { tmp = new byte[indexTypeSize]; buffer.get(tmp, offset, indexTypeSize); facesIndices[i] = readChar(tmp); } } else if (PLY.TYPE_UINT == ((PLYPropertyListDescription) property).getValueType()) { for (int i = 0; i < facesIndices.length; i++) { tmp = new byte[indexTypeSize]; buffer.get(tmp, offset, indexTypeSize); facesIndices[i] = readInt(tmp, fileDescriptor.getFileFormat()); } } else if (PLY.TYPE_USHORT == ((PLYPropertyListDescription) property).getValueType()) { for (int i = 0; i < facesIndices.length; i++) { tmp = new byte[indexTypeSize]; buffer.get(tmp, offset, indexTypeSize); facesIndices[i] = readShort(tmp, fileDescriptor.getFileFormat()); } } else { throw new IOException( "Invalid face index type " + ((PLYPropertyListDescription) property).getValueType() + " for faces description " + property.getName()); } } catch (Exception e) { throw new IOException("Cannot read face: " + e.getMessage(), e); } } else { throw new IOException("Unexpected end of file while reading face vertex indices"); } // Reading face texture indexes list } else if (plyTexCoordPropName.equals(property.getName())) { // Read texture coordinates count for the given face if (PLY.TYPE_CHAR == ((PLYPropertyListDescription) property).getCountType()) { bytes = new byte[PLY.getTypeSize(PLY.TYPE_CHAR)]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (!eof) { textureCoordinatesCount = readChar(bytes); } } else if (PLY.TYPE_UCHAR == ((PLYPropertyListDescription) property).getCountType()) { bytes = new byte[PLY.getTypeSize(PLY.TYPE_UCHAR)]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (!eof) { textureCoordinatesCount = readUChar(bytes); } } else if (PLY.TYPE_SHORT == ((PLYPropertyListDescription) property).getCountType()) { bytes = new byte[PLY.getTypeSize(PLY.TYPE_SHORT)]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (!eof) { textureCoordinatesCount = readShort(bytes, fileDescriptor.getFileFormat()); } } else if (PLY.TYPE_USHORT == ((PLYPropertyListDescription) property).getCountType()) { bytes = new byte[PLY.getTypeSize(PLY.TYPE_USHORT)]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (!eof) { textureCoordinatesCount = readShort(bytes, fileDescriptor.getFileFormat()); } } else if (PLY.TYPE_INT == ((PLYPropertyListDescription) property).getCountType()) { bytes = new byte[PLY.getTypeSize(PLY.TYPE_INT)]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (!eof) { textureCoordinatesCount = readInt(bytes, fileDescriptor.getFileFormat()); } } else if (PLY.TYPE_UINT == ((PLYPropertyListDescription) property).getCountType()) { bytes = new byte[PLY.getTypeSize(PLY.TYPE_UINT)]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (!eof) { textureCoordinatesCount = readInt(bytes, fileDescriptor.getFileFormat()); } } else { throw new IOException( "Unknown PLY count type " + ((PLYPropertyListDescription) property).getCountType() + " for texture coordinates list description"); } // Reading coordinates if (textureCoordinatesCount == 2 * faceVerticesCount) { textureCoordinates = JeometryFactory.createPoint2DContainer(textureCoordinatesCount); double[] values = new double[textureCoordinatesCount]; for (int i = 0; i < textureCoordinatesCount; i++) { bytes = new byte[PLY.getTypeSize(((PLYPropertyListDescription) property).getValueType())]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (eof) { throw new IOException("Unexpected end of file while reading texture coordinate."); } if (PLY.TYPE_CHAR == ((PLYPropertyListDescription) property).getValueType()) { values[i] = readChar(bytes); } else if (PLY.TYPE_UCHAR == ((PLYPropertyListDescription) property).getValueType()) { values[i] = readUChar(bytes); } else if (PLY.TYPE_SHORT == ((PLYPropertyListDescription) property).getValueType()) { values[i] = readShort(bytes, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_USHORT == ((PLYPropertyListDescription) property).getValueType()) { values[i] = readShort(bytes, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_INT == ((PLYPropertyListDescription) property).getValueType()) { values[i] = readInt(bytes, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_UINT == ((PLYPropertyListDescription) property).getValueType()) { values[i] = readInt(bytes, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_FLOAT == ((PLYPropertyListDescription) property).getValueType()) { values[i] = readFloat(bytes, fileDescriptor.getFileFormat()); } else if (PLY.TYPE_DOUBLE == ((PLYPropertyListDescription) property).getValueType()) { values[i] = readDouble(bytes, fileDescriptor.getFileFormat()); } else { throw new IOException("Unsupported property value type " + ((PLYPropertyListDescription) property).getValueType()); } } // Converting read values Point2D point2D = null; for (int i = 0; i < values.length - 1; i = i + 2) { if (isFlipTextureY()) { if (getPoint2DClass() != null) { if (Point2D.class.isAssignableFrom(getPoint2DClass())) { try { point2D = getPoint2DClass().getDeclaredConstructor().newInstance(); point2D.setX(values[i]); point2D.setY(1.0f - values[i + 1]); textureCoordinates.add(point2D); } catch (InstantiationException e) { throw new IOException("Declared point class " + getPoint2DClass() + " cannot be instanciated", e); } catch (IllegalAccessException e) { throw new IOException("Declared point class " + getPoint2DClass() + " cannot be instanciated", e); } catch (Exception e) { throw new IOException("Declared point class " + getPoint2DClass() + " cannot be instanciated", e); } } } else { textureCoordinates.add(JeometryFactory.createPoint2D(values[i], 1.0f - values[i + 1])); } } else { if (getPoint2DClass() != null) { if (Point2D.class.isAssignableFrom(getPoint2DClass())) { try { point2D = getPoint2DClass().getDeclaredConstructor().newInstance(); point2D.setX(values[i]); point2D.setY(values[i + 1]); textureCoordinates.add(point2D); } catch (InstantiationException e) { throw new IOException("Declared point class " + getPoint2DClass() + " cannot be instanciated", e); } catch (IllegalAccessException e) { throw new IOException("Declared point class " + getPoint2DClass() + " cannot be instanciated", e); } catch (Exception e) { throw new IOException("Declared point class " + getPoint2DClass() + " cannot be instanciated", e); } } } else { textureCoordinates.add(JeometryFactory.createPoint2D(values[i], values[i + 1])); } } point2D = null; } } else { logger.log(Level.WARNING, "Unsupported texture coordinates size " + textureCoordinatesCount + ", expected 2D texture with " + 2 * faceVerticesCount + " coordinates"); // Cleaning buffer for (int i = 0; i < textureCoordinatesCount; i++) { bytes = new byte[PLY.getTypeSize(property.getType())]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (eof) { throw new IOException("Unexpected end of file while reading texture coordinate."); } } } } else { // Skipping unknown property and discarding the involved bytes bytes = new byte[PLY.getTypeSize(property.getType())]; lastReadBytes = is.read(bytes); eof = (lastReadBytes < 0); if (eof) { throw new IOException("Unexpected end of file while reading " + property.getName()); } } } // Create face with vertex indices if (facesIndices != null) { // Read a triangle if (facesIndices.length == 3) { face = JeometryFactory.createTexturedIndexedTriangle(facesIndices[0], facesIndices[1], facesIndices[2]); if (face != null) { if ((face instanceof Identified)) { ((Identified)face).setIdentification(faceCurrentIndex); } if ((face instanceof Named)) { ((Named)face).setName(faceTriangleName); } } faceCurrentIndex++; } else { if (textureCoordinates != null) { face = JeometryFactory.createTexturedIndexedMeshFace(facesIndices); if (face != null) { if ((face instanceof Identified)) { ((Identified)face).setIdentification(faceCurrentIndex); } if ((face instanceof Named)) { ((Named)face).setName(faceGenericName); } } faceCurrentIndex++; } else { face = JeometryFactory.createIndexedMeshFace(facesIndices); if (face != null) { if ((face instanceof Identified)) { ((Identified)face).setIdentification(faceCurrentIndex); } if ((face instanceof Named)) { ((Named)face).setName(faceGenericName); } } faceCurrentIndex++; } } } else { throw new IOException("Face with no vertex indices is not supported."); } // Adding texture if available if ((face != null) && (face instanceof Texturable) && (textureCoordinates != null)) { if (textureCoordinates != null) { ((Texturable) face).setTextureCoodinates(textureCoordinates); if ((textures != null) && (textures.size() > 0)) { ((Texturable) face).setTexture(textures.get(textureIndex)); } } } } else { throw new IOException("No property descriptor available for element " + description.getName()); } } else { throw new IOException("No element description available."); } } else { throw new IOException("No input stream available."); } return face; } private boolean accept(Vector point) { // return Geom3D.contains(box, point); return true; } private int readChar(byte[] bytes) { return bytes[0]; } private int readUChar(byte[] bytes) { return (bytes[0] & 0xFF); } private short readShort(byte[] bytes, int byteOrder) { // create a byte buffer and wrap the array ByteBuffer bb = ByteBuffer.wrap(bytes); // if the file uses little endian as apposed to network // (big endian, Java's native) format, // then set the byte order of the ByteBuffer if (byteOrder == PLY.FILE_FORMAT_BINARY_LE) { bb.order(ByteOrder.LITTLE_ENDIAN); } else if (byteOrder == PLY.FILE_FORMAT_BINARY_BE) { bb.order(ByteOrder.BIG_ENDIAN); } return bb.getShort(); } private int readInt(byte[] bytes, int byteOrder) { // create a byte buffer and wrap the array ByteBuffer bb = ByteBuffer.wrap(bytes); // if the file uses little endian as apposed to network // (big endian, Java's native) format, // then set the byte order of the ByteBuffer if (byteOrder == PLY.FILE_FORMAT_BINARY_LE) { bb.order(ByteOrder.LITTLE_ENDIAN); } else if (byteOrder == PLY.FILE_FORMAT_BINARY_BE) { bb.order(ByteOrder.BIG_ENDIAN); } return bb.getInt(); } private float readFloat(byte[] bytes, int byteOrder) { // create a byte buffer and wrap the array ByteBuffer bb = ByteBuffer.wrap(bytes); // if the file uses little endian as apposed to network // (big endian, Java's native) format, // then set the byte order of the ByteBuffer if (byteOrder == PLY.FILE_FORMAT_BINARY_LE) { bb.order(ByteOrder.LITTLE_ENDIAN); } else if (byteOrder == PLY.FILE_FORMAT_BINARY_BE) { bb.order(ByteOrder.BIG_ENDIAN); } return bb.getFloat(); } private double readDouble(byte[] bytes, int byteOrder) { // create a byte buffer and wrap the array ByteBuffer bb = ByteBuffer.wrap(bytes); // if the file uses little endian as apposed to network // (big endian, Java's native) format, // then set the byte order of the ByteBuffer if (byteOrder == PLY.FILE_FORMAT_BINARY_LE) { bb.order(ByteOrder.LITTLE_ENDIAN); } else if (byteOrder == PLY.FILE_FORMAT_BINARY_BE) { bb.order(ByteOrder.BIG_ENDIAN); } return bb.getDouble(); } /* private String readLine(Reader reader) throws IOException { if (reader != null) { int readInt = reader.read(); char character = 0; StringBuffer buffer = new StringBuffer(); boolean eol = false; while ((readInt != -1) && (!eol)) { character = (char) readInt; if (character == '\r') { // eol = true; readInt = reader.read(); } else if (character == '\n') { eol = true; } else { buffer.append(character); readInt = reader.read(); } } return buffer.toString(); } return null; } */ private String readLine(InputStream is) throws IOException { if (is != null) { int readInt = is.read(); char character = 0; StringBuffer buffer = new StringBuffer(); boolean eol = false; while ((readInt != -1) && (!eol)) { character = (char) readInt; if (character == '\r') { // eol = true; readInt = is.read(); } else if (character == '\n') { eol = true; } else { buffer.append(character); readInt = is.read(); } } return buffer.toString(); } return null; } /** * Get the default name applied to vertex if needed. * @return the default name applied to vertex if needed. * @see #setDefaultPointName(String) */ public String getDefaultPointName() { return pointName; } /** * Set the default name applied to vertex if needed. * @param name the default name applied to vertex if needed. * @see #getDefaultPointName() */ public void setDefaultPointName(String name) { pointName = name; } /** * Get the binary size of the given type. * * @param plyType * the type. * @return the binary size of the type. */ public int getBinaryTypeSize(int plyType) { if (PLY.TYPE_CHAR == plyType) { return PLY.TYPE_CHAR_SIZE; } else if (PLY.TYPE_DOUBLE == plyType) { return PLY.TYPE_DOUBLE_SIZE; } else if (PLY.TYPE_FLOAT == plyType) { return PLY.TYPE_FLOAT_SIZE; } else if (PLY.TYPE_INT == plyType) { return PLY.TYPE_INT_SIZE; } else if (PLY.TYPE_SHORT == plyType) { return PLY.TYPE_SHORT_SIZE; } else if (PLY.TYPE_UCHAR == plyType) { return PLY.TYPE_UCHAR_SIZE; } else if (PLY.TYPE_UINT == plyType) { return PLY.TYPE_UINT_SIZE; } else if (PLY.TYPE_USHORT == plyType) { return PLY.TYPE_USHORT_SIZE; } return -1; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy