/// <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); }
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; } }