Beispiel #1
0
        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));
        }
Beispiel #2
0
        /// <summary>
        /// Parses the file bytes to a 3d model info.
        /// </summary>
        /// <param name="fileData">Loaded file bytes with compatible format.</param>
        /// <returns>Mesh3D object if the provided file has valid bytes and format.</returns>
        public static Mesh3D ParseModelFile(ModelFileData fileData)
        {
            switch (fileData.FileType)
            {
            case Supported3DFiles.STL_Binary:
                return(ParseSTLBinary(fileData));

            case Supported3DFiles.STL_ASCII:
                return(ParseSTLASCII(fileData));

            case Supported3DFiles._3MF:
                return(Parse3MF(fileData));

            case Supported3DFiles.OBJ:
                return(ParseWavefront(fileData));

            default:
                return(null);
            }
        }
Beispiel #3
0
        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);
            }
        }
Beispiel #4
0
        static Mesh3D Parse3MF(ModelFileData filedData)
        {
#if !X64
            // The library is not supported for no x64 builds.
            return(null);
#else
            // 3MF (3D Manufacturing Format): https://github.com/3MFConsortium/spec_core/blob/master/3MF%20Core%20Specification.md
            // File loading using Lib3MF: https://github.com/3MFConsortium/lib3mf

            List <int>            trianglesList = new List <int>();
            List <Mesh3D.Vertexh> vertexList    = new List <Mesh3D.Vertexh>();

            Lib3MF.CModel model = Lib3MF.Wrapper.CreateModel();
            model.QueryReader("3mf").ReadFromFile(filedData.FileFullPath);

            var meshIterator        = model.GetMeshObjects();
            int lastMeshVertexIndex = 0;

            while (meshIterator.MoveNext())
            {
                var resource = meshIterator.GetCurrent();

                Lib3MF.CMeshObject meshobject = model.GetMeshObjectByID(resource.GetResourceID());

                int triangleAmount = (int)meshobject.GetTriangleCount();
                if (triangleAmount == 0)
                {
                    continue;
                }

                for (int i = 0; i < triangleAmount; i++)
                {
                    var triangle = meshobject.GetTriangle((uint)i);

                    int index0 = trianglesList.Count;
                    for (int j = 0; j < triangle.Indices.Length; j++)
                    {
                        if (j >= 3)
                        {  // Trick to triangulate polygons with more than 3 vertex.
                            trianglesList.Add(trianglesList[index0]);
                            trianglesList.Add(trianglesList[trianglesList.Count - 2]);
                        }
                        trianglesList.Add(lastMeshVertexIndex + (int)triangle.Indices[j]);
                    }
                }

                var vertexAmount = meshobject.GetVertexCount();
                for (int i = 0; i < vertexAmount; i++)
                {
                    var vertex = meshobject.GetVertex((uint)i);
                    vertexList.Add(new Mesh3D.Vertexh((Half)vertex.Coordinates[0], (Half)vertex.Coordinates[1], (Half)vertex.Coordinates[2]));
                }

                lastMeshVertexIndex = vertexList.Count;
            }

            if (trianglesList.Count == 0 || vertexList.Count == 0)
            {
                return(null);
            }

            SegmentedArray <Mesh3D.Vertexh> verticesArray  = new SegmentedArray <Mesh3D.Vertexh>(vertexList.Count);
            SegmentedArray <int>            trianglesArray = new SegmentedArray <int>(trianglesList.Count);
            for (int i = 0; i < vertexList.Count; i++)
            {
                verticesArray[i] = vertexList[i];
            }

            for (int i = 0; i < trianglesList.Count; i++)
            {
                trianglesArray[i] = trianglesList[i];
            }

            return(new Mesh3D(verticesArray, trianglesArray));
#endif
        }
Beispiel #5
0
        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));
        }
Beispiel #6
0
        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);
            }
        }