540 lines
21 KiB
C#
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;
|
|
}
|
|
}
|
|
} |