using System.Collections.Generic; using System.Drawing; using global::System.IO; using System.Linq; using global::Assimp; using global::Assimp.Unmanaged; namespace Pilz.S3DFileParser.AssimpModule { internal class ConversionContext { public Scene DaeMdl { get; set; } public UpAxis UpAxis { get; set; } public Mesh NewMesh { get; set; } = new Mesh(); public Object3D NewObj { get; set; } = new Object3D(); public Dictionary ChannelIndicies { get; set; } = new Dictionary(); public ConversionContext(Scene daeMdlP, UpAxis upAxis) { DaeMdl = daeMdlP; UpAxis = upAxis; NewObj.Meshes.Add(NewMesh); } } internal class ParsingContext { public ConversionContext ConversionContext { get; private set; } public Dictionary DicVertices { get; set; } = new Dictionary(); public Dictionary DicNormals { get; set; } = new Dictionary(); public Dictionary DicUVs { get; set; } = new Dictionary(); public Dictionary DicVertexColors { get; set; } = new Dictionary(); public ParsingContext(ConversionContext ctx) { ConversionContext = ctx; } } public class AssimpLoader { public static event LoadingAssimpLibsEventHandler LoadingAssimpLibs; public delegate void LoadingAssimpLibsEventHandler(); public static event LoadedAssimpLibsEventHandler LoadedAssimpLibs; public delegate void LoadedAssimpLibsEventHandler(); public static string PathToAssimpLib32 { get; set; } = "Assimp32.dll"; public static string PathToAssimpLib64 { get; set; } = "Assimp64.dll"; internal static void LoadAssimpLibs() { if (!AssimpLibrary.Instance.IsLibraryLoaded) { LoadingAssimpLibs?.Invoke(); AssimpLibrary.Instance.LoadLibrary(PathToAssimpLib32, PathToAssimpLib64); LoadedAssimpLibs?.Invoke(); } } private static void ComputeNewObj(ConversionContext cc) { var identity = Matrix4x4.Identity; ComputeNode(cc.DaeMdl.RootNode, identity, new ParsingContext(cc)); } private static void ComputeNode(Node node, Matrix4x4 trafo, ParsingContext pc) { var newObj = pc.ConversionContext.NewObj; var newMesh = pc.ConversionContext.NewMesh; var daeMdl = pc.ConversionContext.DaeMdl; var channelIndicies = pc.ConversionContext.ChannelIndicies; var UpAxis = pc.ConversionContext.UpAxis; var dicVertices = pc.DicVertices; var dicNormals = pc.DicNormals; var dicUVs = pc.DicUVs; var dicVertexColors = pc.DicVertexColors; trafo = trafo * node.Transform; if (node.HasMeshes) { foreach (var meshIndex in node.MeshIndices) { var m = daeMdl.Meshes[meshIndex]; Material curMat; if (m.MaterialIndex > -1 && newObj.Materials.Count > m.MaterialIndex) { curMat = newObj.Materials.ElementAt(m.MaterialIndex).Value; } else { curMat = null; } foreach (Vector3D untransformedN in m.Normals) { var n = trafo * untransformedN; if (!dicNormals.ContainsKey(n)) { var newNormal = new Normal(); switch (UpAxis) { case UpAxis.Y: { newNormal.X = n.X; newNormal.Y = n.Y; newNormal.Z = n.Z; break; } case UpAxis.Z: { newNormal.X = n.Y; newNormal.Y = n.Z; newNormal.Z = n.X; break; } } newMesh.Normals.Add(newNormal); dicNormals.Add(n, newNormal); } } foreach (Vector3D untranformedV in m.Vertices) { var v = trafo * untranformedV; if (!dicVertices.ContainsKey(v)) { var newVert = new Vertex(); switch (UpAxis) { case UpAxis.Y: { newVert.X = v.X; newVert.Y = v.Y; newVert.Z = v.Z; break; } case UpAxis.Z: { newVert.X = v.Y; newVert.Y = v.Z; newVert.Z = v.X; break; } } newMesh.Vertices.Add(newVert); dicVertices.Add(v, newVert); } } foreach (List uvList in m.TextureCoordinateChannels) { foreach (Vector3D uv in uvList) { if (!dicUVs.ContainsKey(uv)) { var newUV = new UV(); newUV.U = uv.X; newUV.V = uv.Y; newMesh.UVs.Add(newUV); dicUVs.Add(uv, newUV); } } } foreach (List vcList in m.VertexColorChannels) { foreach (Color4D vc in vcList) { if (!dicVertexColors.ContainsKey(vc)) { var newVC = new VertexColor(); newVC.R = vc.R; newVC.G = vc.G; newVC.B = vc.B; newVC.A = vc.A; newMesh.VertexColors.Add(newVC); dicVertexColors.Add(vc, newVC); } } } foreach (Assimp.Face f in m.Faces) { if (f.HasIndices) { var newFace = new Face() { Material = curMat }; foreach (int index in f.Indices) { if (index > -1) { var newPoint = new Point(); if (m.HasVertices) { var v = trafo * m.Vertices[index]; newPoint.Vertex = dicVertices[v]; } if (m.HasNormals) { var n = trafo * m.Normals[index]; newPoint.Normal = dicNormals[n]; } if (curMat is object && channelIndicies.ContainsKey(curMat)) { int tkey = channelIndicies[curMat]; if (m.HasTextureCoords(tkey)) { newPoint.UV = dicUVs[m.TextureCoordinateChannels[tkey][index]]; } if (m.HasVertexColors(tkey)) { newPoint.VertexColor = dicVertexColors[m.VertexColorChannels[tkey][index]]; } } newFace.Points.Add(newPoint); } } if (newFace.Points.Count == 3) { newMesh.Faces.Add(newFace); } } } } } foreach (var n in node.Children) ComputeNode(n, trafo, pc); } public static Object3D FromFile(string fileName, bool LoadMaterials, UpAxis UpAxis) { var LoadedImages = new Dictionary(); var ac = new AssimpContext(); var cc = new ConversionContext(ac.ImportFile(fileName, PostProcessPreset.TargetRealTimeMaximumQuality | PostProcessSteps.Triangulate), UpAxis); var newObj = cc.NewObj; var daeMdl = cc.DaeMdl; var channelIndicies = cc.ChannelIndicies; foreach (EmbeddedTexture et in daeMdl.Textures) { if (et.HasCompressedData) { var newMat = new Material(); var ms = new MemoryStream(et.CompressedData); newMat.Image = Image.FromStream(ms); ms.Close(); newObj.Materials.Add("tex_" + daeMdl.Textures.IndexOf(et), newMat); } } foreach (Assimp.Material mat in daeMdl.Materials) { var newMat = new Material(); TextureSlot? texSlot = default; Color4D? col4d = default; newMat.Opacity = mat.Opacity; switch (true) { case object _ when mat.HasTextureNormal: { texSlot = mat.TextureNormal; break; } case object _ when mat.HasTextureDiffuse: { texSlot = mat.TextureDiffuse; break; } case object _ when mat.HasTextureAmbient: { texSlot = mat.TextureAmbient; break; } case object _ when mat.HasTextureSpecular: { texSlot = mat.TextureSpecular; break; } } switch (true) { case object _ when mat.HasColorDiffuse: { col4d = mat.ColorDiffuse; break; } case object _ when mat.HasColorAmbient: { col4d = mat.ColorAmbient; break; } case object _ when mat.HasColorSpecular: { col4d = mat.ColorSpecular; break; } } if (texSlot is object) { string filePath = texSlot.Value.FilePath; if (LoadMaterials) { if (!string.IsNullOrEmpty(filePath)) { string combiPath = Path.Combine(Path.GetDirectoryName(fileName), filePath); if (File.Exists(combiPath)) { newMat.Image = LoadImage(combiPath, LoadedImages); } else if (File.Exists(filePath)) { newMat.Image = LoadImage(filePath, LoadedImages); } } else if (texSlot.Value.TextureIndex > -1 && daeMdl.Textures.Count > texSlot.Value.TextureIndex) { var et = daeMdl.Textures[texSlot.Value.TextureIndex]; if (et.HasCompressedData) { var ms = new MemoryStream(et.CompressedData); newMat.Image = Image.FromStream(ms); ms.Close(); } } } channelIndicies.Add(newMat, texSlot.Value.UVIndex); } if (col4d is object) { newMat.Color = Color.FromArgb((int)(col4d.Value.R * 255f), (int)(col4d.Value.G * 255f), (int)(col4d.Value.B * 255f)); } newObj.Materials.Add(mat.Name, newMat); } ComputeNewObj(cc); return newObj; } public static void ToFile(string fileName, Object3D obj) { var mdl = new Scene(); var dicMatIndex = new Dictionary(); string texDir = ""; if (obj.Materials.Count > 0) { texDir = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName)); if (!Directory.Exists(texDir)) { Directory.CreateDirectory(texDir); } } foreach (KeyValuePair kvp in obj.Materials) { var mat = new Assimp.Material(); mat.Name = !string.IsNullOrEmpty(kvp.Key) ? kvp.Key : "_" + mdl.Materials.Count; mat.Opacity = mat.Opacity; var texslot = new TextureSlot(); texslot.TextureIndex = mdl.Textures.Count; texslot.TextureType = TextureType.Diffuse; texslot.UVIndex = 0; var ms = new MemoryStream(); kvp.Value.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Png); // Dim tex As New EmbeddedTexture("png", ms.GetBuffer) ms.Close(); if (kvp.Value.Image is object) { texslot.FilePath = Path.Combine(texDir, mat.Name + ".png"); File.WriteAllBytes(texslot.FilePath, ms.GetBuffer()); } // mdl.Textures.Add(tex) mat.AddMaterialTexture(ref texslot); mdl.Materials.Add(mat); if (kvp.Value.Color is object) { { var withBlock = kvp.Value.Color.Value; mat.ColorDiffuse = new Color4D((float)(withBlock.R / 255d), (float)(withBlock.G / 255d), (float)(withBlock.B / 255d), 1f); } } dicMatIndex.Add(kvp.Value, mdl.Materials.Count - 1); } var dicTexMesh = new Dictionary(); var dicMeshDicVertIndex = new Dictionary>(); var dicCounter = new Dictionary(); foreach (Mesh mesh in obj.Meshes) { foreach (Face f in mesh.Faces) { Assimp.Mesh m; if (dicTexMesh.ContainsKey(f.Material)) { m = dicTexMesh[f.Material]; } else { m = new Assimp.Mesh("Mesh_" + (mdl.MeshCount + 1)); m.PrimitiveType = PrimitiveType.Triangle; if (dicMatIndex.ContainsKey(f.Material)) { m.MaterialIndex = dicMatIndex[f.Material]; } mdl.Meshes.Add(m); dicTexMesh.Add(f.Material, m); dicMeshDicVertIndex.Add(m, new Dictionary()); dicCounter.Add(m, 0); } var newFace = new Assimp.Face(); foreach (Point p in f.Points) { newFace.Indices.Add(dicCounter[m]); if (p.Vertex is object) { var vert = new Vector3D(); vert.X = (float)p.Vertex.X; vert.Y = (float)p.Vertex.Y; vert.Z = (float)p.Vertex.Z; m.Vertices.Add(vert); } else { m.Vertices.Add(new Vector3D(0f, 0f, 0f)); } if (p.Normal is object) { var norm = new Vector3D(); norm.X = p.Normal.X; norm.Y = p.Normal.Y; norm.Z = p.Normal.Z; m.Normals.Add(norm); } else { m.Normals.Add(new Vector3D(0f, 0f, 0f)); } // If p.UV IsNot Nothing Then // Dim uv As New Vector3D // uv.X = p.UV.U // uv.Y = p.UV.V // m.TextureCoordinateChannels(0).Add(uv) // Else // m.TextureCoordinateChannels(0).Add(New Vector3D(0, 0, 0)) // End If // If p.VertexColor IsNot Nothing Then // Dim vc As New Color4D // vc.R = p.VertexColor.R // vc.G = p.VertexColor.G // vc.B = p.VertexColor.B // vc.A = p.VertexColor.A // m.VertexColorChannels(0).Add(vc) // Else // m.VertexColorChannels(0).Add(New Color4D(0, 0, 0, 0)) // End If dicCounter[m] += 1; } m.Faces.Add(newFace); } } // Add Root Node mdl.RootNode = new Node(Path.GetFileName(fileName)); // Add Mesh Indicies for (int i = 0, loopTo = mdl.MeshCount - 1; i <= loopTo; i++) mdl.RootNode.MeshIndices.Add(i); var ac = new AssimpContext(); string formatID = ""; string myExt = Path.GetExtension(fileName).ToLower().Substring(1); foreach (ExportFormatDescription efd in ac.GetSupportedExportFormats()) { if ((myExt ?? "") == (efd.FileExtension ?? "")) { formatID = efd.FormatId; break; } } ac.ExportFile(mdl, fileName, formatID); } private static Image LoadImage(string fileName, Dictionary loadedImages) { if (File.Exists(fileName)) { if (loadedImages.ContainsKey(fileName)) { return loadedImages[fileName]; } else { var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); var img = Image.FromStream(fs); fs.Close(); foreach (var kvp in loadedImages) { if (((Bitmap)img).IsTheSameAs((Bitmap)kvp.Value)) { return kvp.Value; } } loadedImages.Add(fileName, img); return img; } } return null; } } }