static Mesh3D ParseSTLASCII(ModelFileData fileData) { // STL ASCII Format: https://en.wikipedia.org/wiki/STL_(file_format)#Binary_STL if (!fileData.HasBytes()) { return(null); } List <Mesh3D.Vertexh> vertices = new List <Mesh3D.Vertexh>(); try { Stream fileDataStream = fileData.GetStream(); fileDataStream.Position = 0; StreamReader reader = new StreamReader(fileDataStream); while (!reader.EndOfStream) { string line = reader.ReadLine().TrimStart(); if (!line.StartsWith("vertex")) { continue; } for (int i = 0; i < 3; i++) { var matches = Regex.Matches(line, FloatStringPattern); vertices.Add(new Mesh3D.Vertexh( Half.Parse(matches[0].Value), Half.Parse(matches[1].Value), Half.Parse(matches[2].Value) )); line = reader.ReadLine().TrimStart(); } } } catch (Exception ex) { logger.Trace(ex, "Exception when processing file: {FileName}, {FileSizeKB}, {FileType}", fileData.FileName, fileData.FileSizeKB, fileData.FileType.ToString()); return(null); } SegmentedArray <Mesh3D.Vertexh> verticesArray = new SegmentedArray <Mesh3D.Vertexh>(vertices.Count); SegmentedArray <int> trianglesArray = new SegmentedArray <int>(vertices.Count); for (int i = 0; i < vertices.Count; i++) { verticesArray[i] = vertices[i]; trianglesArray[i] = i; } return(new Mesh3D(verticesArray, trianglesArray)); }
public static Supported3DFiles CheckFileType(ModelFileData fileData) { byte[] byteBuffer = new byte[100]; if (fileData.HasBytes()) { // Has enough bytes? if (!fileData.ReadBytes(byteBuffer, 0, 0, byteBuffer.Length)) { return(Supported3DFiles.Unsuportted); } // Check if some evil being renamed some PNG file to one of the supported formats. if (byteBuffer[0] == 137 && byteBuffer[1] == 80 && byteBuffer[2] == 78 && byteBuffer[3] == 71 && byteBuffer[4] == 13 && byteBuffer[5] == 10 && byteBuffer[6] == 26 && byteBuffer[7] == 10) { return(Supported3DFiles.Unsuportted); } // Or maybe a JPG. if (byteBuffer[0] == 0xFF && byteBuffer[1] == 0xD8 && byteBuffer[2] == 0xFF) { return(Supported3DFiles.Unsuportted); } } if (fileData.FileName.EndsWith(".stl", StringComparison.InvariantCultureIgnoreCase)) { if (!fileData.HasBytes()) { return(Supported3DFiles.STL_Binary); // Return default STL format. } // If doesnt start with "solid", its always binary. if (!(Encoding.ASCII.GetString(byteBuffer, 0, 5) == "solid")) { return(Supported3DFiles.STL_Binary); } // EXCEPT, that some binary files also start with solid, so check the header and wish bad omen to whoever created such files. string header = Encoding.ASCII.GetString(byteBuffer, 0, 100); for (int i = 0; i < header.Length; i++) { if (Char.IsControl(header[i]) && header[i] != '\r' && header[i] != '\n') { return(Supported3DFiles.STL_Binary); } } return(Supported3DFiles.STL_ASCII); } else if (fileData.FileName.EndsWith(".obj", StringComparison.InvariantCultureIgnoreCase)) { return(Supported3DFiles.OBJ); } if (fileData.FileName.EndsWith(".3mf", StringComparison.InvariantCultureIgnoreCase)) { return(Supported3DFiles._3MF); } else { return(Supported3DFiles.Unsuportted); } }
static Mesh3D ParseWavefront(ModelFileData fileData) { // Wavefront OBJ Format: https://en.wikipedia.org/wiki/Wavefront_.obj_file if (!fileData.HasBytes()) { return(null); } List <Mesh3D.Vertexh> vertices = new List <Mesh3D.Vertexh>(); List <int> triangles = new List <int>(); try { Stream fileDataStream = fileData.GetStream(); fileDataStream.Position = 0; StreamReader reader = new StreamReader(fileDataStream); while (!reader.EndOfStream) { string line = reader.ReadLine().TrimStart(); if (string.IsNullOrWhiteSpace(line)) { continue; } if (line.StartsWith("v ")) { var matches = Regex.Matches(line, FloatStringPattern); if (matches.Count >= 6) { // Dodge the vertex color (or is it supposed to come after x y z? Wikipedia is not clear about it). vertices.Add(new Mesh3D.Vertexh( Half.Parse(matches[0].Value), Half.Parse(matches[2].Value), Half.Parse(matches[4].Value) )); } else { vertices.Add(new Mesh3D.Vertexh( Half.Parse(matches[0].Value), Half.Parse(matches[1].Value), Half.Parse(matches[2].Value) )); } } else if (line.StartsWith("f")) { IEnumerable <string> faceIndexes = line.Split(' ').Where(s => s != "f" && !string.IsNullOrWhiteSpace(s)); int index0 = triangles.Count; // First index added for this line. for (int i = 0; i < faceIndexes.Count(); i++) { int.TryParse(faceIndexes.ElementAt(i).Split('/').First(), out int parsedIndex); // Handle the face as a triangle fan to support faces with more than 3 vertex (idea from https://notes.underscorediscovery.com/obj-parser-easy-parse-time-triangulation/). if (i >= 3) { triangles.Add(triangles[index0]); triangles.Add(triangles[triangles.Count - 2]); } triangles.Add(parsedIndex - 1); // -1 because our array starts at 0 while the OBJ starts at 1. } } else { continue; } } } catch (Exception ex) { logger.Trace(ex, "Exception when processing file: {FileName}, {FileSizeKB}, {FileType}", fileData.FileName, fileData.FileSizeKB, fileData.FileType.ToString()); return(null); } SegmentedArray <Mesh3D.Vertexh> verticesArray = new SegmentedArray <Mesh3D.Vertexh>(vertices.Count); SegmentedArray <int> trianglesArray = new SegmentedArray <int>(triangles.Count); for (int i = 0; i < vertices.Count; i++) { verticesArray[i] = vertices[i]; } for (int i = 0; i < triangles.Count; i++) { trianglesArray[i] = triangles[i]; } return(new Mesh3D(verticesArray, trianglesArray)); }
static Mesh3D ParseSTLBinary(ModelFileData fileData) { // STL Binary Format: https://en.wikipedia.org/wiki/STL_(file_format)#Binary_STL if (!fileData.HasBytes()) { return(null); } byte[] byteBuffer = new byte[4 * 9]; int index = 80; int byteBufferIndex = 0; fileData.ReadBytes(byteBuffer, index, 0, 4); int triangleAmount = (int)System.BitConverter.ToUInt32(byteBuffer, 0); index += 4; if (triangleAmount <= 0 || fileData.BytesLength < 80 + triangleAmount * 50) { return(null); } try { if (triangleAmount <= 0 || triangleAmount > 10000000) { throw new Exception("The STLBinary file has too many triangles."); } SegmentedArray <int> triangles = new SegmentedArray <int>(triangleAmount * 3); SegmentedArray <Mesh3D.Vertexh> vertices = new SegmentedArray <Mesh3D.Vertexh>(triangleAmount * 3); for (int i = 0; i < triangles.Length;) { // Skip reading the facet normal (why STL come with normals???). index += 4 * 3; // Triangle vertices { fileData.ReadBytes(byteBuffer, index, 0, byteBuffer.Length); byteBufferIndex = 0; index += 4 * 9; vertices[i] = new Mesh3D.Vertexh( (Half)System.BitConverter.ToSingle(byteBuffer, byteBufferIndex), (Half)System.BitConverter.ToSingle(byteBuffer, byteBufferIndex += 4), (Half)System.BitConverter.ToSingle(byteBuffer, byteBufferIndex += 4) ); triangles[i] = i++; vertices[i] = new Mesh3D.Vertexh( (Half)System.BitConverter.ToSingle(byteBuffer, byteBufferIndex += 4), (Half)System.BitConverter.ToSingle(byteBuffer, byteBufferIndex += 4), (Half)System.BitConverter.ToSingle(byteBuffer, byteBufferIndex += 4) ); triangles[i] = i++; vertices[i] = new Mesh3D.Vertexh( (Half)System.BitConverter.ToSingle(byteBuffer, byteBufferIndex += 4), (Half)System.BitConverter.ToSingle(byteBuffer, byteBufferIndex += 4), (Half)System.BitConverter.ToSingle(byteBuffer, byteBufferIndex += 4) ); triangles[i] = i++; } // Skip the remaining bytes. index += 2; } return(new Mesh3D(vertices, triangles)); } catch (Exception ex) { logger.Trace(ex, "Exception when processing file: {FileName}, {FileSizeKB}, {FileType}", fileData.FileName, fileData.FileSizeKB, fileData.FileType.ToString()); return(null); } }