Files
Pilz/Pilz.Simple3DFileParser/FileParser/AssimpLoader.cs
2020-09-24 11:21:53 +02:00

540 lines
21 KiB
C#

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<Material, int> ChannelIndicies { get; set; } = new Dictionary<Material, int>();
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<Vector3D, Vertex> DicVertices { get; set; } = new Dictionary<Vector3D, Vertex>();
public Dictionary<Vector3D, Normal> DicNormals { get; set; } = new Dictionary<Vector3D, Normal>();
public Dictionary<Vector3D, UV> DicUVs { get; set; } = new Dictionary<Vector3D, UV>();
public Dictionary<Color4D, VertexColor> DicVertexColors { get; set; } = new Dictionary<Color4D, VertexColor>();
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<Vector3D> 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<Color4D> 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<string, Image>();
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<Material, int>();
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<string, Material> 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<Material, Assimp.Mesh>();
var dicMeshDicVertIndex = new Dictionary<Assimp.Mesh, Dictionary<Vertex, int>>();
var dicCounter = new Dictionary<Assimp.Mesh, int>();
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<Vertex, int>());
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<string, Image> 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;
}
}
}