/// <summary>
        /// Reads the model in ASCII format from the specified stream.
        /// </summary>
        private ImportedModelContainer TryReadAscii(Stream stream, StlImportOptions importOptions)
        {
            using (var reader = new StreamReader(stream, ENCODING, false, 128, true))
            {
                VertexStructure newStructure = new VertexStructure();
                while (!reader.EndOfStream)
                {
                    var line = reader.ReadLine();
                    if (line == null)
                    {
                        continue;
                    }

                    line = line.Trim();
                    if (line.Length == 0 || line.StartsWith("\0") || line.StartsWith("#") || line.StartsWith("!") ||
                        line.StartsWith("$"))
                    {
                        continue;
                    }

                    string id, values;
                    ParseLine(line, out id, out values);
                    switch (id)
                    {
                    // Header.. not needed here
                    case "solid":
                        break;

                    // Geometry data
                    case "facet":
                        this.ReadFacet(reader, values, newStructure, importOptions);
                        break;

                    // End of file
                    case "endsolid":
                        break;
                    }
                }

                // Generate result container
                ImportedModelContainer result         = new ImportedModelContainer(importOptions);
                NamedOrGenericKey      geoResourceKey = result.GetResourceKey(
                    RES_KEY_GEO_CLASS, RES_KEY_GEO_NAME);
                result.ImportedResources.Add(new ImportedResourceInfo(
                                                 geoResourceKey,
                                                 () => new GeometryResource(newStructure)));
                GenericObject geoObject = new GenericObject(geoResourceKey);
                result.Objects.Add(geoObject);

                // Append an object which transform the whole coordinate system
                ScenePivotObject rootObject = result.CreateAndAddRootObject();
                result.ParentChildRelationships.Add(new Tuple <SceneObject, SceneObject>(rootObject, geoObject));

                return(result);
            }
        }
        /// <summary>
        /// Reads the model from the specified binary stream.
        /// </summary>
        private ImportedModelContainer TryReadBinary(Stream stream, StlImportOptions importOptions)
        {
            // Check length
            long length = stream.Length;

            if (length < 84)
            {
                throw new SeeingSharpException("Incomplete file (smaller that 84 bytes)");
            }

            // Read number of triangles
            uint numberTriangles = 0;

            using (var reader = new BinaryReader(stream, Encoding.GetEncoding("us-ascii"), true))
            {
                // Read header (is not needed)
                //  (solid stands for Ascii format)
                string header = ENCODING.GetString(reader.ReadBytes(80), 0, 80).Trim();
                if (header.StartsWith("solid", StringComparison.OrdinalIgnoreCase))
                {
                    return(null);
                }

                // Read and check number of triangles
                numberTriangles = ReadUInt32(reader);
                if (length - 84 != numberTriangles * 50)
                {
                    throw new SeeingSharpException("Incomplete file (smaller that expected byte count)");
                }

                // Read geometry data
                VertexStructure newStructure = new VertexStructure((int)numberTriangles * 3);
                newStructure.CreateSurface((int)numberTriangles);
                for (int loop = 0; loop < numberTriangles; loop++)
                {
                    this.ReadTriangle(reader, newStructure, importOptions);
                }

                // Generate result container
                ImportedModelContainer result         = new ImportedModelContainer(importOptions);
                NamedOrGenericKey      geoResourceKey = result.GetResourceKey(
                    RES_KEY_GEO_CLASS, RES_KEY_GEO_NAME);
                result.ImportedResources.Add(new ImportedResourceInfo(
                                                 geoResourceKey,
                                                 () => new GeometryResource(newStructure)));
                GenericObject geoObject = new GenericObject(geoResourceKey);
                result.Objects.Add(geoObject);

                // Append an object which transform the whole coordinate system
                ScenePivotObject rootObject = result.CreateAndAddRootObject();
                result.ParentChildRelationships.Add(new Tuple <SceneObject, SceneObject>(rootObject, geoObject));

                return(result);
            }
        }
        public async Task LoadAndRender_StlFile()
        {
            await TestUtilities.InitializeWithGraphicsAsync();

            using (var memRenderTarget = new MemoryRenderTarget(1024, 1024))
            {
                memRenderTarget.ClearColor = Color4.CornflowerBlue;

                // Get and configure the camera
                var camera = (PerspectiveCamera3D)memRenderTarget.Camera;
                camera.Position = new Vector3(-4f, 4f, -4f);
                camera.Target   = new Vector3(2f, 0f, 2f);
                camera.UpdateCamera();

                // Import Fox model
                var importOptions = new StlImportOptions
                {
                    ResourceCoordinateSystem = CoordinateSystem.LeftHanded_UpZ,
                    FitToCube = false
                };

                var loadedObjects = await memRenderTarget.Scene.ImportAsync(
                    TestUtilities.CreateResourceLink("Models", "Fox.stl"),
                    importOptions);

                // Wait for it to be visible
                await memRenderTarget.Scene.WaitUntilVisibleAsync(loadedObjects, memRenderTarget.RenderLoop);

                // Take screenshot
                var screenshot = await memRenderTarget.RenderLoop.GetScreenshotGdiAsync();

                //TestUtilities.DumpToDesktop(screenshot, "Blub.png");

                // Calculate and check difference
                var isNearEqual = BitmapComparison.IsNearEqual(
                    screenshot, TestUtilities.LoadBitmapFromResource("ModelLoadingAndRendering", "ModelStl.png"));
                Assert.IsTrue(isNearEqual, "Difference to reference image is to big!");
            }

            // Finishing checks
            Assert.IsTrue(GraphicsCore.Current.MainLoop !.RegisteredRenderLoopCount == 0, "RenderLoops where not disposed correctly!");
        }
        /// <summary>
        /// Imports a model from the given file.
        /// </summary>
        /// <param name="importOptions">Some configuration for the importer.</param>
        /// <param name="sourceFile">The source file to be loaded.</param>
        public ImportedModelContainer ImportModel(ResourceLink sourceFile, ImportOptions importOptions)
        {
            // Get import options
            StlImportOptions stlImportOptions = importOptions as StlImportOptions;

            if (stlImportOptions == null)
            {
                throw new SeeingSharpException("Invalid import options for StlImporter!");
            }

            ImportedModelContainer result = null;

            try
            {
                // Try to read in BINARY format first
                using (Stream inStream = sourceFile.OpenInputStream())
                {
                    result = this.TryReadBinary(inStream, stlImportOptions);
                }

                // Read in ASCII format (if binary did not work)
                if (result == null)
                {
                    using (Stream inStream = sourceFile.OpenInputStream())
                    {
                        result = this.TryReadAscii(inStream, stlImportOptions);
                    }
                }
            }
            catch (Exception ex)
            {
                throw new SeeingSharpException($"Unable to read Stl file {sourceFile}: {ex.Message}!", ex);
            }

            // Handle empty result (unknown format error)
            if (result == null)
            {
                throw new SeeingSharpException($"Unable to read Stl file {sourceFile}: Unrecognized format error!");
            }

            return(result);
        }
Exemple #5
0
        public async Task LoadAndRender_StlFile()
        {
            await UnitTestHelper.InitializeWithGrahicsAsync();

            using (MemoryRenderTarget memRenderTarget = new MemoryRenderTarget(1024, 1024))
            {
                memRenderTarget.ClearColor = Color4.CornflowerBlue;

                // Get and configure the camera
                PerspectiveCamera3D camera = memRenderTarget.Camera as PerspectiveCamera3D;
                camera.Position = new Vector3(-4f, 4f, -4f);
                camera.Target   = new Vector3(2f, 0f, 2f);
                camera.UpdateCamera();

                // Import Fox model
                StlImportOptions importOptions = new StlImportOptions();
                importOptions.ResourceCoordinateSystem = CoordinateSystem.LeftHanded_UpZ;
                IEnumerable <SceneObject> loadedObjects = await memRenderTarget.Scene.ImportAsync(
                    new AssemblyResourceLink(
                        typeof(ModelLoadingAndRenderingTests),
                        "Ressources.Models.Fox.stl"),
                    importOptions);

                // Wait for it to be visible
                await memRenderTarget.Scene.WaitUntilVisibleAsync(loadedObjects, memRenderTarget.RenderLoop);

                // Take screenshot
                GDI.Bitmap screenshot = await memRenderTarget.RenderLoop.GetScreenshotGdiAsync();

                // Calculate and check difference
                bool isNearEqual = BitmapComparison.IsNearEqual(
                    screenshot, Properties.Resources.ReferenceImage_ModelStl);
                Assert.True(isNearEqual, "Difference to reference image is to big!");
            }

            // Finishing checks
            Assert.True(GraphicsCore.Current.MainLoop.RegisteredRenderLoopCount == 0, "RenderLoops where not disposed correctly!");
        }
        /// <summary>
        /// Reads the model from the specified binary stream.
        /// </summary>
        private ImportedModelContainer?TryReadBinary(ResourceLink source, Stream stream, StlImportOptions importOptions)
        {
            // Check length
            var length = stream.Length;

            if (length < 84)
            {
                throw new SeeingSharpException("Incomplete file (smaller that 84 bytes)");
            }

            // Read number of triangles
            using (var reader = new BinaryReader(stream, Encoding.GetEncoding("us-ascii"), true))
            {
                // Read header (is not needed)
                //  (solid stands for Ascii format)
                var header = s_encoding.GetString(reader.ReadBytes(80), 0, 80).Trim();

                if (header.StartsWith("solid", StringComparison.OrdinalIgnoreCase))
                {
                    return(null);
                }

                // Read and check number of triangles
                var numberTriangles = ReadUInt32(reader);
                if (length - 84 != numberTriangles * 50)
                {
                    throw new SeeingSharpException("Incomplete file (smaller that expected byte count)");
                }

                // Read geometry data
                var newGeometry = new Geometry((int)numberTriangles * 3);
                newGeometry.CreateSurface((int)numberTriangles);

                for (var loop = 0; loop < numberTriangles; loop++)
                {
                    ReadTriangle(reader, newGeometry, importOptions);
                }

                // Generate result container
                var modelContainer = new ImportedModelContainer(source, importOptions);
                var resGeometryKey = modelContainer.GetResourceKey(RES_KEY_GEO_CLASS, RES_KEY_GEO_NAME);
                var resMaterialKey = modelContainer.GetResourceKey(RES_KEY_MAT_CLASS, RES_KEY_MAT_NAME);
                modelContainer.AddResource(new ImportedResourceInfo(
                                               resGeometryKey,
                                               _ => new GeometryResource(newGeometry)));
                modelContainer.AddResource(new ImportedResourceInfo(
                                               resMaterialKey,
                                               _ => new StandardMaterialResource()));
                var loadedMesh = new Mesh(resGeometryKey, resMaterialKey);
                modelContainer.AddObject(loadedMesh);

                // Append an object which transform the whole coordinate system
                modelContainer.FinishLoading(newGeometry.GenerateBoundingBox());

                return(modelContainer);
            }
        }
        /// <summary>
        /// Reads the model in ASCII format from the specified stream.
        /// </summary>
        private ImportedModelContainer TryReadAscii(ResourceLink source, Stream stream, StlImportOptions importOptions)
        {
            using (var reader = new StreamReader(stream, s_encoding, false, 128, true))
            {
                var newGeometry = new Geometry();

                while (!reader.EndOfStream)
                {
                    var line = reader.ReadLine();

                    if (line == null)
                    {
                        continue;
                    }

                    line = line.Trim();

                    if (line.Length == 0 || line.StartsWith("\0") || line.StartsWith("#") || line.StartsWith("!") ||
                        line.StartsWith("$"))
                    {
                        continue;
                    }

                    ParseLine(line, out var id, out var values);
                    switch (id)
                    {
                    // Header.. not needed here
                    case "solid":
                        break;

                    // Geometry data
                    case "facet":
                        this.ReadFacet(reader, values, newGeometry, importOptions);
                        break;

                    // End of file
                    case "endsolid":
                        break;
                    }
                }

                // Generate result container
                var modelContainer = new ImportedModelContainer(source, importOptions);
                var resGeometryKey = modelContainer.GetResourceKey(RES_KEY_GEO_CLASS, RES_KEY_GEO_NAME);
                var resMaterialKey = modelContainer.GetResourceKey(RES_KEY_MAT_CLASS, RES_KEY_MAT_NAME);
                modelContainer.AddResource(new ImportedResourceInfo(
                                               resGeometryKey,
                                               _ => new GeometryResource(newGeometry)));
                modelContainer.AddResource(new ImportedResourceInfo(
                                               resMaterialKey,
                                               _ => new StandardMaterialResource()));
                var loadedMesh = new Mesh(resGeometryKey, resMaterialKey);
                modelContainer.AddObject(loadedMesh);

                // Append an object which transform the whole coordinate system
                modelContainer.FinishLoading(newGeometry.GenerateBoundingBox());

                return(modelContainer);
            }
        }
        /// <summary>
        /// Reads a triangle from a binary STL file.
        /// </summary>
        private static void ReadTriangle(BinaryReader reader, Geometry geometry, StlImportOptions importOptions)
        {
            var ni     = ReadFloat(reader);
            var nj     = ReadFloat(reader);
            var nk     = ReadFloat(reader);
            var normal = new Vector3(ni, nj, nk);

            var x1 = ReadFloat(reader);
            var y1 = ReadFloat(reader);
            var z1 = ReadFloat(reader);
            var v1 = new Vector3(x1, y1, z1);

            var x2 = ReadFloat(reader);
            var y2 = ReadFloat(reader);
            var z2 = ReadFloat(reader);
            var v2 = new Vector3(x2, y2, z2);

            var x3 = ReadFloat(reader);
            var y3 = ReadFloat(reader);
            var z3 = ReadFloat(reader);
            var v3 = new Vector3(x3, y3, z3);

            // Try to read color information
            var attrib       = Convert.ToString(ReadUInt16(reader), 2).PadLeft(16, '0').ToCharArray();
            var hasColor     = attrib[0].Equals('1');
            var currentColor = Color.Transparent;

            if (hasColor)
            {
                var blue = attrib[15].Equals('1') ? 1 : 0;
                blue = attrib[14].Equals('1') ? blue + 2 : blue;
                blue = attrib[13].Equals('1') ? blue + 4 : blue;
                blue = attrib[12].Equals('1') ? blue + 8 : blue;
                blue = attrib[11].Equals('1') ? blue + 16 : blue;
                var b = blue * 8;

                var green = attrib[10].Equals('1') ? 1 : 0;
                green = attrib[9].Equals('1') ? green + 2 : green;
                green = attrib[8].Equals('1') ? green + 4 : green;
                green = attrib[7].Equals('1') ? green + 8 : green;
                green = attrib[6].Equals('1') ? green + 16 : green;
                var g = green * 8;

                var red = attrib[5].Equals('1') ? 1 : 0;
                red = attrib[4].Equals('1') ? red + 2 : red;
                red = attrib[3].Equals('1') ? red + 4 : red;
                red = attrib[2].Equals('1') ? red + 8 : red;
                red = attrib[1].Equals('1') ? red + 16 : red;
                var r = red * 8;

                currentColor = new Color(Convert.ToByte(r), Convert.ToByte(g), Convert.ToByte(b));
            }

            var targetSurface = geometry.FirstSurface;

            if (importOptions.IsChangeTriangleOrderNeeded())
            {
                targetSurface.AddTriangle(
                    new VertexBasic(v3, currentColor, Vector2.Zero, normal),
                    new VertexBasic(v2, currentColor, Vector2.Zero, normal),
                    new VertexBasic(v1, currentColor, Vector2.Zero, normal));
            }
            else
            {
                targetSurface.AddTriangle(
                    new VertexBasic(v1, currentColor, Vector2.Zero, normal),
                    new VertexBasic(v2, currentColor, Vector2.Zero, normal),
                    new VertexBasic(v3, currentColor, Vector2.Zero, normal));
            }
        }
        /// <summary>
        /// Reads a facet.
        /// </summary>
        private void ReadFacet(StreamReader reader, string normalString, Geometry newGeometry, StlImportOptions importOptions)
        {
            _cachedPoints.Clear();

            // Read all geometry
            var normal = ParseNormal(normalString);

            ReadLine(reader, "outer");

            while (true)
            {
                var line = reader.ReadLine();
                if (line == null)
                {
                    throw new SeeingSharpException("Unexpected end of file!");
                }

                if (TryParseVertex(line, out var point))
                {
                    _cachedPoints.Add(point);
                    continue;
                }

                ParseLine(line, out var id, out _);

                if (id == "endloop")
                {
                    break;
                }
            }

            // Read end
            ReadLine(reader, "endfacet");

            // Overtake geometry data
            var targetSurface = newGeometry.FirstSurface;
            var pointCount    = _cachedPoints.Count;

            switch (_cachedPoints.Count)
            {
            case 0:
            case 1:
            case 2:
                break;

            case 3:
                if (importOptions.IsChangeTriangleOrderNeeded())
                {
                    targetSurface.AddTriangle(
                        new VertexBasic(_cachedPoints[2], Color4.Transparent, Vector2.Zero, normal),
                        new VertexBasic(_cachedPoints[1], Color4.Transparent, Vector2.Zero, normal),
                        new VertexBasic(_cachedPoints[0], Color4.Transparent, Vector2.Zero, normal));
                }
                else
                {
                    targetSurface.AddTriangle(
                        new VertexBasic(_cachedPoints[0], Color4.Transparent, Vector2.Zero, normal),
                        new VertexBasic(_cachedPoints[1], Color4.Transparent, Vector2.Zero, normal),
                        new VertexBasic(_cachedPoints[2], Color4.Transparent, Vector2.Zero, normal));
                }
                break;

            default:
                var indices = new int[pointCount];
                if (importOptions.IsChangeTriangleOrderNeeded())
                {
                    for (var loop = pointCount - 1; loop > -1; loop--)
                    {
                        indices[loop] = newGeometry.AddVertex(
                            new VertexBasic(_cachedPoints[loop], Color4.Transparent, Vector2.Zero, normal));
                    }
                }
                else
                {
                    for (var loop = 0; loop < pointCount; loop++)
                    {
                        indices[loop] = newGeometry.AddVertex(
                            new VertexBasic(_cachedPoints[loop], Color4.Transparent, Vector2.Zero, normal));
                    }
                }

                targetSurface.AddPolygonByCuttingEars(indices);
                break;
            }
        }
        /// <summary>
        /// Reads a facet.
        /// </summary>
        private void ReadFacet(StreamReader reader, string normalString, VertexStructure newStructure, StlImportOptions importOptions)
        {
            m_cachedPoints.Clear();

            // Read all geometry
            Vector3 normal = ParseNormal(normalString);

            ReadLine(reader, "outer");
            while (true)
            {
                string  line = reader.ReadLine();
                Vector3 point;
                if (TryParseVertex(line, out point))
                {
                    m_cachedPoints.Add(point);
                    continue;
                }

                string id, values;
                ParseLine(line, out id, out values);

                if (id == "endloop")
                {
                    break;
                }
            }

            // Read end
            ReadLine(reader, "endfacet");

            // Overtake geometry data
            VertexStructureSurface targetSurfae = newStructure.FirstSurface;
            int pointCount = m_cachedPoints.Count;

            switch (m_cachedPoints.Count)
            {
            case 0:
            case 1:
            case 2:
                break;

            case 3:
                if (importOptions.IsChangeTriangleOrderNeeded())
                {
                    targetSurfae.AddTriangle(
                        new Vertex(m_cachedPoints[2], Color4.Transparent, Vector2.Zero, normal),
                        new Vertex(m_cachedPoints[1], Color4.Transparent, Vector2.Zero, normal),
                        new Vertex(m_cachedPoints[0], Color4.Transparent, Vector2.Zero, normal));
                }
                else
                {
                    targetSurfae.AddTriangle(
                        new Vertex(m_cachedPoints[0], Color4.Transparent, Vector2.Zero, normal),
                        new Vertex(m_cachedPoints[1], Color4.Transparent, Vector2.Zero, normal),
                        new Vertex(m_cachedPoints[2], Color4.Transparent, Vector2.Zero, normal));
                }
                break;

            default:
                int[] indices = new int[pointCount];
                if (importOptions.IsChangeTriangleOrderNeeded())
                {
                    for (int loop = pointCount - 1; loop > -1; loop--)
                    {
                        indices[loop] = newStructure.AddVertex(
                            new Vertex(m_cachedPoints[loop], Color4.Transparent, Vector2.Zero, normal));
                    }
                }
                else
                {
                    for (int loop = 0; loop < pointCount; loop++)
                    {
                        indices[loop] = newStructure.AddVertex(
                            new Vertex(m_cachedPoints[loop], Color4.Transparent, Vector2.Zero, normal));
                    }
                }

                targetSurfae.AddPolygonByCuttingEars(indices);
                break;
            }
        }